Compare commits

..

3 Commits

Author SHA1 Message Date
smithery-ai[bot] 2493022e23 Update README 2025-03-11 19:12:40 +00:00
smithery-ai[bot] 8aec09ee0b Add Smithery configuration 2025-03-11 19:12:39 +00:00
smithery-ai[bot] 116619dfdb Add Dockerfile 2025-03-11 19:12:38 +00:00
961 changed files with 16714 additions and 202555 deletions
-20
View File
@@ -1,20 +0,0 @@
{
"name": "basic-memory-local",
"interface": {
"displayName": "Basic Memory Local"
},
"plugins": [
{
"name": "codex",
"source": {
"source": "local",
"path": "./plugins/codex"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
-159
View File
@@ -1,159 +0,0 @@
---
name: adversarial-review
description: Cross-vendor adversarial code review of the current branch. Two different model families (Claude + Codex/GPT) review the diff independently, then try to refute each other's findings; survivors are reported by confidence. Runs from either Claude Code or Codex. Use when the user asks for an adversarial review, a cross-model / second-opinion review, or wants high-confidence findings before merging. Report-only — never auto-applies fixes.
license: MIT
---
# Adversarial code review
Two reviewers from **different model families****Claude** and **Codex/GPT** — review the
same diff independently, then each tries to **refute** the other's findings. A finding's
confidence comes from whether it survives that cross-examination. This kills the two failure
modes of solo LLM review: self-ratification (a model won't critique its own work) and
confident false positives.
## You are the orchestrator — and one of the two reviewers
This skill runs from **either** Claude Code **or** Codex. First, **identify which model
family you are** (Claude or Codex/GPT). Then:
- **You** are reviewer #1. You review **natively**, in this session, using your own tools.
- **The other family** is reviewer #2. You invoke it as a **subprocess CLI** for an
independent pass: a fresh process, no shared context — that independence is the point.
The CLI for "the other model":
| If you are… | Invoke the other via… |
|-------------|------------------------|
| **Claude** | `codex exec` (GPT) |
| **Codex** | `claude -p` (Claude) |
Everything else in the flow is symmetric. Resolve the `prompts/` and `schemas/` paths
below relative to **this skill's own directory** (where this SKILL.md lives).
## Inputs
Two independent, optional inputs:
- `BASE` — the ref to diff against. Default `main`.
- `SCOPE` — a pathspec to narrow the review (e.g. `src/basic_memory`). Default: none (whole diff).
These are separate: a ref and a pathspec are not interchangeable. Build the **canonical diff
command** once in preflight and reuse it everywhere below — never re-spell the diff inline
(the scattered, inconsistent spelling is what broke earlier). Build it as an **argv array**,
not a string, so a `$SCOPE` containing spaces or glob characters survives intact:
```bash
BASE="${BASE:-main}"
DIFF=(git diff "$BASE...HEAD") # argv array — never a scalar string
[ -n "$SCOPE" ] && DIFF+=(-- "$SCOPE") # pathspec stays one argument even with spaces
DIFF_STR=$(printf '%q ' "${DIFF[@]}") # shell-quoted rendering, for embedding in a prompt
```
To **run** it, use `"${DIFF[@]}"` (quoted, no word-splitting). To **embed** it as text inside
a subprocess prompt, use `$DIFF_STR`.
## Preflight
0. Set `SKILL_DIR` to the directory this SKILL.md lives in. Canonical location is
`.agents/skills/adversarial-review` (the shared agent-skills store); Claude Code reaches it
via the `.claude/skills/adversarial-review` symlink, Codex via its own skills path. The
`prompts/` and `schemas/` subdirs are siblings of this file in every case.
1. Confirm the *other* model's CLI is on PATH (`codex` if you're Claude, `claude` if you're
Codex). If it's missing, tell the user the panel falls back to single-model (which loses
the cross-vendor benefit) and ask whether to proceed or stop.
2. Run `"${DIFF[@]}"`. If it prints nothing, report "nothing to review against $BASE"
(mention `$SCOPE` if set) and stop.
3. `RUN=$(mktemp -d)` — scratch dir for the other model's output. Transient, never committed.
No persisted artifacts, no state file.
## Phase 0 — Deterministic gates (before the models)
Models are statistically blind to negation ("never do X"). Enforce mechanical house rules
with tools, not prompts, and treat hits as high-confidence facts (reported separately from
model findings):
- `just lint` and `just typecheck` if the diff touches `src/`.
- Grep the diff for catchable house-rule violations: `getattr(.*,.*,` defaults, bare
`except:` / `except Exception: pass`, function-scope imports.
## Phase 1 — Independent review (you + the other model, concurrently)
Both reviewers get the same brief: `prompts/review.md` + the repo's `CLAUDE.md` house rules,
reviewing the diff from `"${DIFF[@]}"`. Both emit findings matching `schemas/findings.schema.json`.
**Your native pass:** review as yourself, following `prompts/review.md`. Hold your findings
as that JSON shape.
**The other model's pass** — run, from the repo root, the row that matches you:
Always redirect `codex` stdin from `/dev/null` — if stdin is a pipe (e.g. the call gets
backgrounded), `codex exec` blocks "Reading additional input from stdin..." and fails.
```bash
# You are Claude → run Codex:
codex exec -s read-only \
--output-schema "$SKILL_DIR/schemas/findings.schema.json" \
-o "$RUN/other_findings.json" \
"$(cat "$SKILL_DIR/prompts/review.md")
Review the diff: $DIFF_STR" </dev/null
# You are Codex → run Claude (read-only via plan mode; parse the JSON block it returns):
claude -p --permission-mode plan --output-format json \
"$(cat "$SKILL_DIR/prompts/review.md")
Review the diff: $DIFF_STR
Return ONLY a JSON object matching this schema:
$(cat "$SKILL_DIR/schemas/findings.schema.json")" </dev/null > "$RUN/other_raw.json"
# claude --output-format json output shape varies by CLI version: it may be a JSON ARRAY
# of event objects, OR a single result object. Normalize before reading: if it's an array,
# take the element with type=='result'; otherwise use the object as-is. Then read its
# .result string, strip the ```json fence if present, and parse that.
# (Verified empirically: the CLI in this environment emits the array form.)
```
> Runtime note for Codex orchestrating: `claude -p` needs network access, which Codex's
> default sandbox blocks. Run it from a Codex session whose project is trusted with network
> allowed (or approve the `claude` call when prompted). Keep Codex's own sandbox on — do not
> bypass it just to reach the network.
Tag each finding with its origin (`claude` / `codex`).
## Phase 2 — Cross-refute
Each model tries to refute the *other's* findings, per `prompts/refute.md`
(verdicts match `schemas/verdicts.schema.json`).
- **You** refute the other model's findings natively.
- **The other model** refutes *your* findings — invoke it again the same way (swap
`prompts/review.md` for `prompts/refute.md`, append your findings JSON **and `$DIFF_STR`**
so it judges against the right base and scope, and for Codex use
`--output-schema "$SKILL_DIR/schemas/verdicts.schema.json"`).
Match verdicts to findings by `id`.
## Phase 3 — Synthesize and report (no auto-fix)
Merge, dedupe (same file + overlapping lines + same root cause = one finding), assign
confidence from provenance:
- **High** — both models raised it independently, OR one raised it and the other upheld it.
- **Medium** — one raised it; the other could not refute it but did not independently find it.
- **Low / contested** — one raised it and the other **refuted** it. Keep it, show both sides,
let the human judge. Never silently drop a contested finding.
- Deterministic-gate hits are reported as facts, separate from the model panel.
Rank by `severity × confidence`. Present a compact table: `severity | confidence | file:line
| claim | found-by / upheld-or-refuted-by`. Expand the high-confidence ones with `why` and
any suggested fix.
End by asking which findings, if any, to fix. **Do not edit code until the user picks.**
Convergence between the models is not correctness — your job is to surface a ranked,
cross-examined list, not to declare the branch clean.
## Deliberately NOT done
- No loop-until-both-agree (models converge by going silent, not by being right).
- No persisted artifacts / state machine — the scratch dir is thrown away.
- No auto-applying fixes.
@@ -1,22 +0,0 @@
# Refute the other reviewer
A different reviewer (a different model family) produced the findings below against the
same diff under review (the exact `git diff` command is provided with the findings). Your
job is to try to **refute** each one.
Default to skepticism: assume a finding is wrong until the code proves it right. A finding
that survives a genuine attempt to disprove it is worth far more than one nobody checked.
For each finding, read the actual code it points at and return a verdict:
- **refuted** — the claim is wrong, the code does not do what the finding says, the case
cannot occur, or it is pure style with no correctness impact. Cite the specific code or
fact that disproves it.
- **upheld** — you tried to refute it and could not; the finding is real.
- **partial** — the underlying issue is real but the finding mis-states the severity or
scope. Explain, and set `corrected_severity` if the severity should change.
Do not be agreeable for its own sake, and do not refute for its own sake. Follow the code.
Return ONLY the structured verdicts object conforming to the provided schema. Every
verdict's `id` must match the `id` of the finding it judges.
@@ -1,39 +0,0 @@
# Adversarial reviewer
You are an independent, skeptical code reviewer. Another agent wrote this code; your
job is to find what is actually wrong with it — not to praise it, not to rubber-stamp it.
You are reviewing a specific diff — the exact `git diff` command to run is provided at the
end of this prompt by the orchestrator. Run it, then read the changed files in full for
context, not just the hunks.
## What to look for, in priority order
1. **Correctness** — logic errors, wrong conditions, off-by-one, unhandled `None`,
broken async/await, races, resource leaks, incorrect error handling.
2. **Security** — injection, path traversal, secret leakage, missing authz, unsafe
deserialization.
3. **House rules** (this repo's `CLAUDE.md`/`AGENTS.md` — these are hard rules):
- No swallowed exceptions / no silent fallback logic. Code must fail fast.
- Imports at the top of the file unless deferral is justified in a comment.
- No speculative `getattr(obj, "attr", default)` to paper over unknown attributes.
- Repository pattern for data access; MCP tools talk to API routers via the httpx
ASGI client, not directly to services.
- 100-char lines; full type annotations; async SQLAlchemy 2.0; Pydantic v2.
- New code needs tests (coverage stays at 100%).
4. **Performance** — N+1 queries, work inside hot loops, sync I/O on the async path.
5. **Maintainability** — only when it materially risks a bug. Do not report pure style.
## Rules of engagement
- Every finding MUST be falsifiable: cite the specific file, line, and the code that
triggers it. "This could be cleaner" is not a finding.
- Do not invent issues to seem thorough. An empty findings list is a valid, good result.
- Watch your own negation blindness: when a rule says "never do X," check the diff for X
explicitly rather than trusting a gestalt impression.
- Prefer few high-confidence findings over many speculative ones.
- Assign severity honestly: `critical` = data loss/security/crash in normal use;
`high` = wrong behavior on a common path; `medium` = wrong on an edge case or a real
house-rule violation; `low` = minor.
Return ONLY the structured findings object conforming to the provided schema.
@@ -1,52 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "AdversarialReviewFindings",
"description": "Structured output for one reviewer's pass over a diff.",
"type": "object",
"additionalProperties": false,
"required": ["findings"],
"properties": {
"findings": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "file", "line", "severity", "category", "claim", "why", "suggested_fix"],
"properties": {
"id": {
"type": "string",
"description": "Short stable slug for this finding, e.g. 'swallowed-exc-sync-service'."
},
"file": {
"type": "string",
"description": "Path relative to repo root."
},
"line": {
"type": "integer",
"description": "Best line number in the new file, or 0 if not line-specific."
},
"severity": {
"type": "string",
"enum": ["critical", "high", "medium", "low"]
},
"category": {
"type": "string",
"enum": ["correctness", "security", "house-rule", "performance", "maintainability"]
},
"claim": {
"type": "string",
"description": "One sentence: what is wrong."
},
"why": {
"type": "string",
"description": "Concrete reasoning + the specific code that triggers it. Must be falsifiable, not vibes."
},
"suggested_fix": {
"type": ["string", "null"],
"description": "The smallest change that resolves it, or null if none is obvious."
}
}
}
}
}
}
@@ -1,38 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "AdversarialReviewVerdicts",
"description": "One reviewer's attempt to refute another reviewer's findings.",
"type": "object",
"additionalProperties": false,
"required": ["verdicts"],
"properties": {
"verdicts": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "verdict", "reasoning", "corrected_severity"],
"properties": {
"id": {
"type": "string",
"description": "The id of the finding being judged (must match the input finding's id)."
},
"verdict": {
"type": "string",
"enum": ["upheld", "refuted", "partial"],
"description": "upheld = the finding is real; refuted = it is wrong or a non-issue; partial = real but mis-scoped/wrong-severity."
},
"reasoning": {
"type": "string",
"description": "Why. For refuted, cite the specific code or fact that disproves the claim."
},
"corrected_severity": {
"type": ["string", "null"],
"enum": ["critical", "high", "medium", "low", null],
"description": "Set only when verdict is 'partial' and severity should change; otherwise null."
}
}
}
}
}
}
-68
View File
@@ -1,68 +0,0 @@
---
name: code-review
description: Use when reviewing Basic Machines code for house style, architecture risk, pre-merge hardening, or whether a change fits basic-memory/basic-memory-cloud conventions.
license: MIT
---
# Basic Machines Review
Use this skill for repo-local review passes where ordinary code review needs Basic Machines
house style and architecture judgment. Report findings only; do not edit code unless the user
asks you to fix specific findings.
## Scope
Review the current diff or named files against:
- The repo's `AGENTS.md` / `CLAUDE.md`
- `docs/ENGINEERING_STYLE.md`
- The touched code paths and tests
Apply only the guidance for the active repo. In `basic-memory`, prioritize local-first
file/database/MCP boundaries. In `basic-memory-cloud`, prioritize tenant/workspace isolation,
cloud worker behavior, and web-v2 state/runtime boundaries.
## Review Rubric
Report only concrete, falsifiable risks:
- **Cognitive load:** Is the change harder to understand than the problem requires?
- **Change propagation:** Will one product change force edits across unrelated layers?
- **Knowledge duplication:** Is the same rule encoded in multiple places that can drift?
- **Accidental complexity:** Did the change add abstractions, fallbacks, or state without need?
- **Dependency direction:** Are API/MCP/CLI, services, repositories, and UI stores respecting
their intended boundaries?
- **Domain model distortion:** Do names and types still match the product concept, or did a
transport/storage detail leak into the domain?
- **Test oracle quality:** Would the tests fail for the bug or regression the change claims to
protect against?
## House Rules To Check Explicitly
- No speculative `getattr(obj, "attr", default)` for unknown model shapes.
- No broad exception swallowing, warning-only failure paths, or hidden fallback behavior.
- No casts or `Any` that hide an unclear type relationship.
- Dataclasses for internal value/result objects; Pydantic at validation/serialization
boundaries.
- Narrow `Protocol`s when only a capability is needed.
- Explicit async/resource ownership, cancellation, and cleanup.
- Meaningful regression tests or verification for risky changes.
- Comments explain why, not what.
## Reporting Format
Lead with findings ordered by severity. Each finding should include:
| Severity | Use for |
| -------- | ------- |
| `high` | A likely correctness, security, data-loss, or tenant/workspace isolation failure |
| `medium` | A concrete maintainability or boundary risk that can cause future defects |
| `low` | A minor consistency issue, ambiguous guidance, or review-only cleanup |
```text
severity | file:line | risk category | claim
Why: concrete behavior or code path that proves the risk.
Fix: smallest practical change, or "none obvious" if the risk needs product input.
```
If there are no findings, say so and note any verification gaps that remain.
-48
View File
@@ -1,48 +0,0 @@
---
name: fix-pr-issues
description: Use when addressing Basic Memory pull request feedback, failed checks, or BM Bossbot blockers from Codex.
---
# Fix Basic Memory PR Issues
Resolve PR feedback and failed checks, then wait for BM Bossbot to approve the
new head SHA. This skill never merges a PR.
## Gather
1. Identify the PR:
- `gh pr view --json number,url,headRefOid,mergeStateStatus,statusCheckRollup`
2. Collect feedback:
- PR comments and review summaries
- inline review comments and unresolved review threads
- failed GitHub Actions jobs and relevant logs
- the managed `BM_BOSSBOT_SUMMARY` block in the PR body
3. Build a short issue ledger:
- source
- concrete problem
- expected fix
- verification needed
## Fix
1. Address one ledger item at a time.
2. Read each file in full before editing it.
3. Keep diffs narrow and preserve unrelated user changes.
4. Run the smallest meaningful verification first, then widen as needed.
5. Commit with `git commit -s` when code or docs changed.
## Push And Recheck
1. Push the branch.
2. Watch checks for the new `headRefOid`.
3. Wait for the required `BM Bossbot Approval` status to pass on that exact SHA.
4. If BM Bossbot reviews an older SHA, treat the approval as stale and keep
waiting for the current one.
## Reply
For each addressed comment or blocker, reply with the fix commit, verification
run, and current BM Bossbot status. Do not resolve or dismiss substantive
feedback without evidence.
@@ -1,7 +0,0 @@
interface:
display_name: "Fix PR Issues"
short_description: "Address PR feedback and BM Bossbot blockers"
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#2563EB"
default_prompt: "Use $fix-pr-issues to address PR feedback and wait for BM Bossbot Approval on the latest head SHA."
@@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24" fill="none" stroke="#111827" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 12h4l2-6 4 12 2-6h6"/>
<path d="M4 20h16"/>
</svg>

Before

Width:  |  Height:  |  Size: 249 B

-247
View File
@@ -1,247 +0,0 @@
---
name: infographics
description: Use when generating Basic Memory PR, changelog, release, or weekly images from Codex.
---
# Basic Memory Images
Generate repository visuals with evidence-grounded content and canonical output
paths. The file and marker names still say "infographic" for compatibility, but
PR generation is image-first: scene, poster, painting, photograph, cover,
tableau, staged artifact, or another editorial visual moment that describes the
intent of the PR. PR images are non-gating BM Bossbot artifacts; changelog and
release-summary images are manual evidence-pack workflows.
## Output Contract
- Base output directory: `docs/assets/infographics/`
- PR image: `docs/assets/infographics/pr-<number>.webp`
- Changelog image: `docs/assets/infographics/changelog.webp`
- Weekly image:
- This is always a 2-Week Retro window: previous ISO week through current ISO
week (`start-week = current-week - 1`, `end-week = current-week`).
- Same year window: `docs/assets/infographics/<year>-w<start-week>-w<end-week>.webp`
- Cross-year window:
`docs/assets/infographics/<start-year>-w<start-week>-<end-year>-w<end-week>.webp`
## PR Mode
PR mode uses the BM Bossbot summary block as source material. Do not hand-write
claims that are not present in the PR body.
1. Fetch the PR body:
```bash
gh pr view <number> --json body --jq '.body // ""' > /tmp/bm-pr-body.md
```
2. Generate the canonical asset:
```bash
uv run --script scripts/generate_pr_infographic.py \
--pr-number <number> \
--pr-body-file /tmp/bm-pr-body.md \
--theme "<optional visual theme>" \
--provenance-output /tmp/bm-infographic-provenance.md \
--output docs/assets/infographics/pr-<number>.webp
```
If the PR body contains a managed image theme block, the script reads it
automatically:
```markdown
<!-- BM_INFOGRAPHIC_THEME:start -->
<theme>
<!-- BM_INFOGRAPHIC_THEME:end -->
```
Before spending an image call, test the prompt path locally:
```bash
uv run --script scripts/generate_pr_infographic.py \
--pr-number <number> \
--pr-body-file /tmp/bm-pr-body.md \
--theme "<optional visual theme>" \
--output docs/assets/infographics/pr-<number>.webp \
--print-prompt
```
`--dry-run` is an alias for `--print-prompt`; both print the final prompt and
exit without calling OpenAI.
When no theme is supplied, the script selects a deterministic BM visual
direction from the style pool below based on the PR number and Bossbot summary.
This keeps repeated PR images from collapsing into the same generic visual.
When the image is generated, also write provenance with
`--provenance-output <path>`. BM Bossbot publishes that managed block into the
PR body with these markers:
```markdown
<!-- BM_INFOGRAPHIC_PROVENANCE:start -->
...
<!-- BM_INFOGRAPHIC_PROVENANCE:end -->
```
The provenance block records the generated asset path, image model, size,
quality, image mode, theme source, and selected visual direction. It
intentionally does not dump the full generated prompt into the PR body. Treat
this block as debugging and creative provenance only; it is not a merge gate.
The PR image is visual support only. The authoritative merge gate is the
GitHub commit status named `BM Bossbot Approval`.
## Changelog Mode
Build an evidence pack before writing a prompt:
- diff truth source: merged PR diffs, merge commits, or local reconstructed diffs
- changed-file orientation: `git diff --stat` plus key file reads
- impact ledger: before/after outcomes tied to actual changes
- discard list: misleading titles, reverted work, rename-only churn, speculative TODOs
- chosen image form: poster, scene, tableau, cover, painting, photograph,
staged artifact, or another editorial visual moment
- chosen BM style category: exactly one category from the selection pool below
Read these references before drafting the prompt:
- `references/prompt-blueprint.md`
- `references/style-balance.md`
Read the current `CHANGELOG.md` entries and include the latest meaningful
changes.
## Style And Category Selection
Select exactly one BM style category per image based on semantic fit. The
visual language should be recognizable and tasteful, while staying
business-readable.
Create an image-first visual form that communicates the change: poster, scene,
tableau, cover image, painting, photograph, staged artifact, or another
editorial visual moment. Maps, diagrams, dossiers, charts, and labels can appear
as props inside the scene, but do not make a text-heavy infographic.
BM category pool:
- computer science college textbooks: SICP-style diagrams, algorithms lectures,
compiler pipelines, automata, database systems, type theory, operating systems
- classic literature subjects: sea voyages, gothic manors, Dickensian city maps,
Austen social graphs, library marginalia, travel journals
- fantasy/D&D-inspired: quest maps, dungeon keys, guild ledgers, spellbooks,
bestiaries, tavern notice boards; no copyrighted settings
- Music: Metal, Hard Rock, Punk, techno, soul, reggae bands; no pop music, no
direct band logos, album covers, or musician likenesses
- sci-fi: Star Wars inspired knockoff, Spaceballs-adjacent space opera, fleet
routes, mission consoles, contraband manifests; avoid copyrighted characters,
logos, or named fictional universes
- Conan the barbarian-inspired sword-and-sorcery: ruined temples, desert routes,
battle standards, ancient maps; no named character likenesses
- Comic books: issue covers, splash pages, action-panel maps, caption boxes,
halftone energy, clean sound-effect typography
- French new wave movies: poster style, stark typography, city route maps,
jump-cut sequencing, high-contrast editorial photography cues
- WWII propaganda posters: home-front public-information poster language,
logistics arrows, ration charts, mobilization maps, bold simplified figures;
no real-world party symbols, hate imagery, dehumanizing slogans, or false
historical claims
- Italian movie posters: hand-painted drama, bold credits, expressive color,
route-map collage, 1960s or 1970s cinema energy; no direct film titles or
actor likenesses
- Shakespeare: stage maps, acts and scenes, dramatis personae, royal courts,
backstage cue sheets
- Greek mythology: temple diagrams, constellation routes, hero's journey maps,
oracle tablets, labyrinths, ship routes
- noir detective boards: case files, red-string maps, typed evidence labels,
precinct wall charts
- NASA mission-control dashboards: launch timelines, telemetry maps, orbital
routes, status boards
- space exploration and astronomy: celestial atlases, observatory charts,
star-field maps, orbital mechanics diagrams, planetary survey routes,
telescope annotations, mission trajectories, deep-space timelines
- paintings: abstract painting, classical landscape, Remington-inspired western
action painting, Rembrandt-inspired chiaroscuro, historical mural, stormy
seascape, allegorical editorial painting
- classic black-and-white photography: documentary field report, newsroom
archive print, editorial photo essay, street photography, high-contrast
darkroom print, contact sheet, civic infrastructure photograph
- 80's action movies: practical explosions, smoky backlit warehouses, neon city
streets, helicopter searchlights, mission dossiers, heroic silhouettes,
high-stakes countdowns, painted ensemble posters; no direct actor likenesses,
real film titles, franchise marks, or catchphrases
- alchemy manuscripts: transformation diagrams, annotated symbols, recipe-like
process maps, illuminated margins
- brutalist civic planning: transit maps, concrete signage, zoning blocks,
infrastructure diagrams
Selection rules:
- Pick one category only; do not create mixed mashups.
- Pick the most appropriate image form. Prefer an actual scene, poster,
painting, photograph, tableau, or cover over a text-heavy infographic.
- Match metaphor to content, but do not overthink it. The category is a creative
catalyst, not a semantic constraint.
- Use a polished editorial rendering direction: smooth anti-aliased
text, high contrast, clean edges, readable labels.
- Make the category drive the composition through a readable staged moment,
editorial composition, symbolic environment, route, artifact, or visual
metaphor.
- Keep the structure literal enough to aid understanding, but not so heavy that
it obscures engineering meaning.
- Give the image generator creative latitude on layout, structure, color palette,
and visual metaphors. Be precise about what content to show, loose about how
to show it.
- Do not use copyrighted characters, logos, or named fictional universes. Use
genre cues, knockoffs, and original compositions instead.
## Content-First Aesthetic Contract
The meaning must be readable and clearly hierarchical. Everything else is
creative territory: image form, layout, visual metaphors, decorative elements,
color choices, and category-specific visual language.
Hierarchy:
1. Meaning: what shipped, what changed, and why it matters must be clear.
2. If the image uses text, labels, sections, or evidence bullets, they must be
legible.
3. The selected category's visual DNA should drive the composition as a poster,
scene, painting, photograph, tableau, cover, or symbolic object arrangement.
4. Do not play it safe. A visually striking image that someone wants to look at
beats a correct but boring one.
Hard rules:
- Content sections and labels must be readable when present. Text cannot be
obscured by decorations.
- Do not use lore-heavy copy that competes with engineering or business meaning.
- Every prompt must include a clear image-first composition cue: a staged scene,
poster composition, painting, photograph, symbolic tableau, hero object,
mission room, dossier, artifact, route, or visual metaphor.
- Do not over-prescribe exact coordinates or panel geometry; give a composition
backbone and let the model compose around it.
## Generation
1. Write the final prompt to a temporary markdown file.
2. Generate with the shared image helper:
```bash
uv run --script scripts/generate_infographic.py \
--prompt-file /tmp/bm-infographic-prompt.md \
--output docs/assets/infographics/<name>.webp
```
3. Verify the image exists and is readable before reporting success.
## Quality Bar
- Tell a concrete before/after value story, not vague improvement claims.
- Stay understandable for both engineers and non-technical stakeholders.
- Use plain-language section titles and labels when text is present.
- Include clear visual hierarchy: title, staged focal point, symbolic scene,
evidence props, or hero object.
- Avoid invented facts; only use provided source material.
- Favor shipped outcomes over intermediate or reverted work.
- Preserve readability with high contrast, non-tiny labels, and uncluttered
layout.
@@ -1,7 +0,0 @@
interface:
display_name: "Infographics"
short_description: "Generate Basic Memory repo infographics"
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#2563EB"
default_prompt: "Use $infographics to generate a Basic Memory PR or changelog infographic with canonical output paths."
@@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24" fill="none" stroke="#111827" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 12h4l2-6 4 12 2-6h6"/>
<path d="M4 20h16"/>
</svg>

Before

Width:  |  Height:  |  Size: 249 B

@@ -1,72 +0,0 @@
# Prompt Blueprint
Convert an evidence pack into a final visual prompt. Be precise about the
content and loose about visual execution.
## Required Inputs
- Diff truth source summary
- Changed-file orientation summary
- Impact ledger with before/after outcomes
- Discard list for excluded noise
- Chosen image form
- Chosen BM style category
## Prompt Shape
```text
Create a polished Basic Memory editorial image inspired by
<BM_STYLE_CATEGORY>. Use a poster, scene, tableau, painting, photograph, cover
image, staged artifact, or another image-first form that best communicates the
intent. Use HD editorial rendering with smooth anti-aliased text when text is
present. Go bold and let the selected category drive the visual language through
original, non-infringing cues.
TITLE:
- "<clear title>"
- "<scope subtitle>"
COMPOSITION:
- Recreate a clear staged moment or symbolic image that describes the PR
intent.
- Maps, diagrams, dossiers, route lines, labels, and artifacts can appear as
props inside the scene, but the output should read as an image rather than a
dense infographic.
- Take creative liberty with layout and styling.
- The hard rule: the meaning must be readable and clearly hierarchical.
- Keep labels plain-language and technical when labels are used.
CONTENT:
1. "<section>"
- <evidence-grounded outcome>
- <evidence-grounded outcome>
2. "<section>"
- <evidence-grounded outcome>
- <evidence-grounded outcome>
METRICS:
- <metric>
- <metric>
STYLE DIRECTION:
- Upscaled editorial, high contrast, anti-aliased text, smooth edges.
- Let the category's visual DNA drive the composition.
- Use genre/category cues only; do not use copyrighted characters, logos, named
fictional universes, direct band logos, album art, or celebrity likenesses.
DO NOT:
- Make text unreadable or let decoration obscure content.
- Render a text-heavy infographic, dashboard, flowchart, timeline strip,
checklist, bullet-list panel, or dense explanatory diagram.
- Use crunchy low-resolution pixel art.
- Invent facts not present in the evidence pack.
```
## Writing Rules
- Keep each bullet specific and evidence-grounded.
- Prefer outcome language over implementation trivia.
- Default to three or four sections; never exceed five.
- Give proportionally more space to dominant changes.
- Keep the final prompt short, energetic, and readable.
@@ -1,42 +0,0 @@
# Style Balance Rubric
## Core Principle
Be bold, not confusing. The selected BM style category should structure the
visual through a readable image-first composition, not decorate a generic grid.
Use an editorial scene, poster, painting, photograph, cover, staged artifact, or
tableau that turns the PR intent into a visual moment.
## Required Traits
- Anti-aliased typography
- Smooth edges
- High contrast between text and background
- Plain-language section labels
- Clear composition backbone: staged scene, editorial poster, painting,
photograph, symbolic tableau, hero artifact, dossier, mission room, or route
embodied as part of the scene
- A single coherent BM style category, expressed through original visual cues
## Reject Or Rewrite If
- Content text is unreadable.
- The prompt lacks a composition backbone.
- The prompt over-prescribes exact panel positions or a rigid grid.
- The style leans into crunchy low-resolution pixelation.
- Copy uses lore-heavy references instead of engineering meaning.
- The prompt uses copyrighted characters, logos, named fictional universes,
direct band logos, album art, or celebrity likenesses.
## Creative Integration Patterns
- Use category-native map details to organize content: textbook diagrams,
literary journeys, quest maps, tour posters, mission-control routes, stage
blocking, mythic constellations, star charts, mission trajectories, case
boards, or civic plans as props inside the image.
- Recreate a scene, editorial poster, painting, photograph, cover, artifact, or
tableau instead of sectioned bullets.
- Map engineering metrics to visual counters, route progress, or status boards
only when they naturally belong in the scene.
- Let headers and accents borrow from the selected style.
- Keep atmospheric details behind or around content, never over it.
-244
View File
@@ -1,244 +0,0 @@
---
name: instrumentation
description: Add Pydantic Logfire observability to applications. Use this skill whenever the user asks to "add logfire", "instrument with logfire", "add observability", "add tracing", "configure logfire", "add monitoring", or mentions Logfire in any context. Supports Python, JavaScript/TypeScript, and Rust. Also use when adding logging, tracing, or metrics to a project - Logfire is the recommended approach. Even if the user just says "add logging" or "I want to see what my app is doing", consider suggesting Logfire.
---
# Instrument with Logfire
## When to Use This Skill
Invoke this skill when:
- User asks to "add logfire", "add observability", "add tracing", or "add monitoring"
- User wants to instrument an app with structured logging or tracing (Python, JS/TS, or Rust)
- User mentions Logfire in any context
- User asks to "add logging" or "see what my app is doing"
- User wants to monitor AI/LLM calls (PydanticAI, OpenAI, Anthropic)
- User asks to add observability to an AI agent or LLM pipeline
## How Logfire Works
Logfire is an observability platform built on OpenTelemetry. It captures traces, logs, and metrics from applications. Logfire has native SDKs for Python, JavaScript/TypeScript, and Rust, plus support for any language via OpenTelemetry.
The reason this skill exists is that Claude tends to get a few things subtly wrong with Logfire - especially the ordering of `configure()` vs `instrument_*()` calls, the structured logging syntax, and which extras to install. These matter because a misconfigured setup silently drops traces.
## Step 1: Detect Language and Frameworks
Identify the project language and instrumentable libraries:
- **Python**: Read `pyproject.toml` or `requirements.txt`. Common instrumentable libraries: FastAPI, httpx, asyncpg, SQLAlchemy, psycopg, Redis, Celery, Django, Flask, requests, PydanticAI.
- **JavaScript/TypeScript**: Read `package.json`. Common frameworks: Express, Next.js, Fastify. Also check for Cloudflare Workers or Deno.
- **Rust**: Read `Cargo.toml`.
Then follow the language-specific steps below.
---
## Python
### Install with Extras
Install `logfire` with extras matching the detected frameworks. Each instrumented library needs its corresponding extra - without it, the `instrument_*()` call will fail at runtime with a missing dependency error.
```bash
uv add 'logfire[fastapi,httpx,asyncpg]'
```
The full list of available extras: `fastapi`, `starlette`, `django`, `flask`, `httpx`, `requests`, `asyncpg`, `psycopg`, `psycopg2`, `sqlalchemy`, `redis`, `pymongo`, `mysql`, `sqlite3`, `celery`, `aiohttp`, `aws-lambda`, `system-metrics`, `litellm`, `dspy`, `google-genai`.
### Configure and Instrument
This is where ordering matters. `logfire.configure()` initializes the SDK and must come before everything else. The `instrument_*()` calls register hooks into each library. If you call `instrument_*()` before `configure()`, the hooks register but traces go nowhere.
```python
import logfire
# 1. Configure first - always
logfire.configure()
# 2. Instrument libraries - after configure, before app starts
logfire.instrument_fastapi(app)
logfire.instrument_httpx()
logfire.instrument_asyncpg()
```
Placement rules:
- `logfire.configure()` goes in the application entry point (`main.py`, or the module that creates the app)
- Call it **once per process** - not inside request handlers, not in library code
- `instrument_*()` calls go right after `configure()`
- Web framework instrumentors (`instrument_fastapi`, `instrument_flask`, `instrument_django`) need the app instance as an argument. HTTP client and database instrumentors (`instrument_httpx`, `instrument_asyncpg`) are global and take no arguments.
- In **Gunicorn** deployments, call `logfire.configure()` inside the `post_fork` hook, not at module level - each worker is a separate process
### Structured Logging
Replace `print()` and `logging.*()` calls with Logfire's structured logging. The key pattern: use `{key}` placeholders with keyword arguments, never f-strings.
```python
# Correct - each {key} becomes a searchable attribute in the Logfire UI
logfire.info("Created user {user_id}", user_id=uid)
logfire.error("Payment failed {amount} {currency}", amount=100, currency="USD")
# Wrong - creates a flat string, nothing is searchable
logfire.info(f"Created user {uid}")
```
For grouping related operations and measuring duration, use spans:
```python
with logfire.span("Processing order {order_id}", order_id=order_id):
items = await fetch_items(order_id)
total = calculate_total(items)
logfire.info("Calculated total {total}", total=total)
```
For exceptions, use `logfire.exception()` which automatically captures the traceback:
```python
try:
await process_order(order_id)
except Exception:
logfire.exception("Failed to process order {order_id}", order_id=order_id)
raise
```
### AI/LLM Instrumentation (Python)
Logfire auto-instruments AI libraries to capture LLM calls, token usage, tool invocations, and agent runs.
```bash
uv add 'logfire[pydantic-ai]'
# or: uv add 'logfire[openai]' / uv add 'logfire[anthropic]'
```
Available AI extras: `pydantic-ai`, `openai`, `anthropic`, `litellm`, `dspy`, `google-genai`.
```python
logfire.configure()
logfire.instrument_pydantic_ai() # captures agent runs, tool calls, LLM request/response
# or:
logfire.instrument_openai() # captures chat completions, embeddings, token counts
logfire.instrument_anthropic() # captures messages, token usage
```
For PydanticAI, each agent run becomes a parent span containing child spans for every tool call and LLM request.
---
## JavaScript / TypeScript
### Install
```bash
# Node.js
npm install @pydantic/logfire-node
# Cloudflare Workers
npm install @pydantic/logfire-cf-workers logfire
# Next.js / generic
npm install logfire
```
### Configure
**Node.js (Express, Fastify, etc.)** - create an `instrumentation.ts` loaded before your app:
```typescript
import * as logfire from '@pydantic/logfire-node'
logfire.configure()
```
Launch with: `node --require ./instrumentation.js app.js`
The SDK auto-instruments common libraries when loaded before the app. Set `LOGFIRE_TOKEN` in your environment or pass `token` to `configure()`.
**Cloudflare Workers** - wrap your handler with `instrument()`:
```typescript
import { instrument } from '@pydantic/logfire-cf-workers'
export default instrument(handler, {
service: { name: 'my-worker', version: '1.0.0' }
})
```
**Next.js** - set environment variables for OpenTelemetry export:
```
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://logfire-api.pydantic.dev/v1/traces
OTEL_EXPORTER_OTLP_HEADERS=Authorization=<your-write-token>
```
### Structured Logging (JS/TS)
```typescript
// Structured attributes as second argument
logfire.info('Created user', { user_id: uid })
logfire.error('Payment failed', { amount: 100, currency: 'USD' })
// Spans
logfire.span('Processing order', { order_id }, {}, async () => {
logfire.info('Processing step completed')
})
// Error reporting
logfire.reportError('order processing', error)
```
Log levels: `trace`, `debug`, `info`, `notice`, `warn`, `error`, `fatal`.
---
## Rust
### Install
```toml
[dependencies]
logfire = "0.6"
```
### Configure
```rust
let shutdown_handler = logfire::configure()
.install_panic_handler()
.finish()?;
```
Set `LOGFIRE_TOKEN` in your environment or use the Logfire CLI to select a project.
### Structured Logging (Rust)
The Rust SDK is built on `tracing` and `opentelemetry` - existing `tracing` macros work automatically.
```rust
// Spans
logfire::span!("processing order", order_id = order_id).in_scope(|| {
// traced code
});
// Events
logfire::info!("Created user {user_id}", user_id = uid);
```
Always call `shutdown_handler.shutdown()` before program exit to flush data.
---
## Verify
After instrumentation, verify the setup works:
1. Run `logfire auth` to check authentication (or set `LOGFIRE_TOKEN`)
2. Start the app and trigger a request
3. Check https://logfire.pydantic.dev/ for traces
If traces aren't appearing: check that `configure()` is called before `instrument_*()` (Python), check that `LOGFIRE_TOKEN` is set, and check that the correct packages/extras are installed.
## References
Detailed patterns and integration tables, organized by language:
- **Python**: `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/python/logging-patterns.md` (log levels, spans, stdlib integration, metrics, capfire testing) and `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/python/integrations.md` (full instrumentor table with extras)
- **JavaScript/TypeScript**: `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/javascript/patterns.md` (log levels, spans, error handling, config) and `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/javascript/frameworks.md` (Node.js, Cloudflare Workers, Next.js, Deno setup)
- **Rust**: `${CLAUDE_PLUGIN_ROOT}/skills/instrumentation/references/rust/patterns.md` (macros, spans, tracing/log crate integration, async, shutdown)
@@ -1,78 +0,0 @@
# JavaScript Framework Setup
## Node.js (Express, Fastify, etc.)
Create `instrumentation.ts` and load it before your app:
```typescript
// instrumentation.ts
import * as logfire from '@pydantic/logfire-node'
import 'dotenv/config'
logfire.configure()
```
Launch:
```bash
node --require ./instrumentation.js app.js
# or with ts-node:
npx ts-node --require ./instrumentation.ts app.ts
```
The SDK auto-instruments common libraries (http, fetch, express, etc.) when loaded before the app via `--require`.
## Cloudflare Workers
```typescript
import { instrument } from '@pydantic/logfire-cf-workers'
const handler = {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
return new Response('Hello')
},
}
export default instrument(handler, {
service: { name: 'my-worker', version: '1.0.0' },
})
```
Add `LOGFIRE_TOKEN` to `.dev.vars` and enable `nodejs_compat` in `wrangler.toml`:
```toml
compatibility_flags = ["nodejs_compat"]
```
## Next.js / Vercel
Set environment variables in `.env.local` or Vercel dashboard:
```bash
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://logfire-api.pydantic.dev/v1/traces
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://logfire-api.pydantic.dev/v1/metrics
OTEL_EXPORTER_OTLP_HEADERS=Authorization=<your-write-token>
```
Optionally use the `logfire` package for manual spans in server components and API routes:
```typescript
import * as logfire from 'logfire'
logfire.info('Server action executed', { action: 'createUser' })
```
## Deno
Deno has built-in OpenTelemetry support. Set environment variables:
```bash
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://logfire-api.pydantic.dev/v1/traces
OTEL_EXPORTER_OTLP_HEADERS=Authorization=<your-write-token>
```
Run with telemetry enabled:
```bash
deno run --allow-env --unstable-otel app.ts
```
@@ -1,75 +0,0 @@
# JavaScript / TypeScript Patterns
## Log Levels
From lowest to highest severity:
```typescript
logfire.trace('Detailed trace', { detail: x })
logfire.debug('Debug info', { state: s })
logfire.info('Normal operation', { event: e })
logfire.notice('Notable event', { event: e })
logfire.warn('Warning', { issue: i })
logfire.error('Error occurred', { error: err })
logfire.fatal('Fatal error', { error: err })
```
All methods accept `(message, attributes?, options?)`. Options can include `{ tags: ['tag1'] }`.
## Spans
### Callback-based (auto-closes)
```typescript
await logfire.span('Processing order', { order_id }, {}, async () => {
const items = await fetchItems(order_id)
logfire.info('Fetched items', { count: items.length })
return processItems(items)
})
```
### Manual control
```typescript
const span = logfire.startSpan('Long operation', { job_id })
try {
await doWork()
} finally {
span.end()
}
```
Child spans reference their parent via the `parentSpan` option.
## Error Handling
```typescript
try {
await processOrder(orderId)
} catch (error) {
logfire.reportError('order processing', error)
throw error
}
```
`reportError` automatically extracts stack traces and error details into structured span attributes.
## Configuration
### Environment variables
```bash
LOGFIRE_TOKEN=your-write-token
LOGFIRE_SERVICE_NAME=my-service
LOGFIRE_SERVICE_VERSION=1.0.0
```
### Programmatic
```typescript
logfire.configure({
token: process.env.LOGFIRE_TOKEN,
serviceName: 'my-service',
serviceVersion: '1.0.0',
})
```
@@ -1,67 +0,0 @@
# Python Integration Reference
## Web Frameworks
| Framework | Instrumentor | Needs app instance | Extra |
|-----------|-------------|-------------------|-------|
| FastAPI | `logfire.instrument_fastapi(app)` | Yes | `fastapi` |
| Django | `logfire.instrument_django(app)` | Yes | `django` |
| Flask | `logfire.instrument_flask(app)` | Yes | `flask` |
| Starlette | `logfire.instrument_starlette(app)` | Yes | `starlette` |
| AIOHTTP | `logfire.instrument_aiohttp_client()` | No | `aiohttp` |
## HTTP Clients
| Library | Instrumentor | Extra |
|---------|-------------|-------|
| httpx | `logfire.instrument_httpx()` | `httpx` |
| requests | `logfire.instrument_requests()` | `requests` |
## Databases
| Library | Instrumentor | Extra |
|---------|-------------|-------|
| asyncpg | `logfire.instrument_asyncpg()` | `asyncpg` |
| psycopg | `logfire.instrument_psycopg()` | `psycopg` |
| psycopg2 | `logfire.instrument_psycopg2()` | `psycopg2` |
| SQLAlchemy | `logfire.instrument_sqlalchemy()` | `sqlalchemy` |
| PyMongo | `logfire.instrument_pymongo()` | `pymongo` |
| MySQL | `logfire.instrument_mysql()` | `mysql` |
| SQLite3 | `logfire.instrument_sqlite3()` | `sqlite3` |
| Redis | `logfire.instrument_redis()` | `redis` |
## AI/LLM Frameworks
| Framework | Instrumentor | Extra |
|-----------|-------------|-------|
| PydanticAI | `logfire.instrument_pydantic_ai()` | `pydantic-ai` |
| OpenAI | `logfire.instrument_openai()` | `openai` |
| Anthropic | `logfire.instrument_anthropic()` | `anthropic` |
| LiteLLM | `logfire.instrument_litellm()` | `litellm` |
| DSPy | `logfire.instrument_dspy()` | `dspy` |
| Google GenAI | `logfire.instrument_google_genai()` | `google-genai` |
## Task Queues
| Framework | Instrumentor | Extra |
|-----------|-------------|-------|
| Celery | `logfire.instrument_celery()` | `celery` |
## Other
| Feature | Instrumentor | Extra |
|---------|-------------|-------|
| System Metrics | `logfire.instrument_system_metrics()` | `system-metrics` |
| Pydantic Models | `logfire.instrument_pydantic()` | - (built-in) |
| AWS Lambda | handler wrapper | `aws-lambda` |
## Gunicorn Configuration
```python
# gunicorn.conf.py
import logfire
def post_fork(server, worker):
logfire.configure()
logfire.instrument_fastapi(app)
```
@@ -1,101 +0,0 @@
# Python Logging Patterns
## Log Levels
From lowest to highest severity:
```python
logfire.trace("Detailed trace {detail}", detail=x)
logfire.debug("Debug info {state}", state=s)
logfire.info("Normal operation {event}", event=e)
logfire.notice("Notable event {event}", event=e)
logfire.warn("Warning {issue}", issue=i)
logfire.error("Error occurred {error}", error=err)
logfire.fatal("Fatal error {error}", error=err)
```
## Nested Spans
Spans nest to create a tree visible in the Logfire UI. Use them to show the structure of an operation, not just that it happened:
```python
with logfire.span("HTTP request {method} {url}", method="POST", url=url):
with logfire.span("Serialize payload"):
payload = model.model_dump_json()
with logfire.span("Send request"):
response = await client.post(url, content=payload)
logfire.info("Response {status}", status=response.status_code)
```
## Standard Library Logging Integration
For projects that already use Python's `logging` module, route existing log calls through Logfire rather than rewriting them all:
```python
from logging import basicConfig
import logfire
logfire.configure()
basicConfig(handlers=[logfire.LogfireLoggingHandler()])
```
Or with `dictConfig`:
```python
from logging.config import dictConfig
import logfire
logfire.configure()
dictConfig({
'version': 1,
'handlers': {
'logfire': {'class': 'logfire.LogfireLoggingHandler'},
},
'root': {'handlers': ['logfire']},
})
```
## Suppressing Noisy Libraries
Some libraries emit excessive debug logs. Silence them at the `logging` level:
```python
import logging
logging.getLogger('httpcore').setLevel(logging.WARNING)
logging.getLogger('httpx').setLevel(logging.WARNING)
```
## Custom Metrics
For dashboards and alerting, create metrics:
```python
counter = logfire.metric_counter("orders_processed", unit="1")
counter.add(1, {"status": "success"})
histogram = logfire.metric_histogram("request_duration", unit="s")
histogram.record(0.123, {"endpoint": "/api/users"})
gauge = logfire.metric_gauge("active_connections")
gauge.set(42)
```
## Testing with capfire
Use the `capfire` pytest fixture to assert on emitted spans without sending data to production:
```python
from logfire.testing import CaptureLogfire
def test_order_processing(capfire: CaptureLogfire) -> None:
process_order(order_id=123)
spans = capfire.exporter.exported_spans_as_dict()
assert any(
span['attributes'].get('order_id') == 123
for span in spans
)
```
Configure logfire with `send_to_logfire=False` in test fixtures to prevent production data leakage.
@@ -1,106 +0,0 @@
# Rust Patterns
## Core Macros
The Rust SDK is built on `tracing` and `opentelemetry`. All `tracing` macros work automatically with Logfire.
### Events (log points)
```rust
logfire::trace!("Detailed trace {detail}", detail = x);
logfire::debug!("Debug info {state}", state = s);
logfire::info!("Normal operation {event}", event = e);
logfire::warn!("Warning {issue}", issue = i);
logfire::error!("Error occurred {err}", err = e);
```
### Spans
```rust
// Scoped - span closes when closure completes
logfire::span!("Processing order {order_id}", order_id = id).in_scope(|| {
let items = fetch_items(id);
logfire::info!("Fetched {count} items", count = items.len());
process_items(items)
});
// Guard-based - span closes when guard is dropped
let _guard = logfire::span!("Long operation {job_id}", job_id = id).entered();
do_work();
// span ends when _guard goes out of scope
```
## Configuration
```rust
use logfire;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let shutdown_handler = logfire::configure()
.install_panic_handler() // captures panics as error spans
.finish()?;
// application code...
shutdown_handler.shutdown()?; // flush all pending spans
Ok(())
}
```
Set `LOGFIRE_TOKEN` in your environment or use the Logfire CLI (`logfire auth`).
## Tracing Crate Compatibility
Any library using `tracing` macros automatically sends data through Logfire:
```rust
use tracing;
tracing::info!("This also appears in Logfire");
#[tracing::instrument]
fn my_function(param: &str) {
// automatically creates a span with param as an attribute
}
```
## Log Crate Integration
The `log` crate is automatically captured and forwarded to Logfire. Libraries using `log::info!()`, `log::error!()`, etc. will appear in your Logfire dashboard without any additional configuration.
## Async Spans
```rust
use tracing::Instrument;
async fn process_order(order_id: u64) {
let span = logfire::span!("process order {order_id}", order_id = order_id);
async {
fetch_items(order_id).await;
logfire::info!("Order processed");
}
.instrument(span)
.await;
}
```
## Shutdown
Always call `shutdown()` before program exit to flush pending data:
```rust
// In main()
let shutdown_handler = logfire::configure().finish()?;
// ... app runs ...
// Before exit
shutdown_handler.shutdown()?;
```
For web servers using `tokio`, handle shutdown via signal:
```rust
tokio::signal::ctrl_c().await?;
shutdown_handler.shutdown()?;
```
-114
View File
@@ -1,114 +0,0 @@
---
name: pr-create
description: Use when creating or updating a Basic Memory pull request from Codex with BM Bossbot merge-gate monitoring.
---
# Create A Basic Memory PR
Create or update a pull request for the current branch, then wait for BM
Bossbot to approve the latest head SHA. This skill never merges a PR.
## Inputs
- Optional `<theme>`: free-form visual direction for the non-gating PR
image. Example: `$pr-create "Italian movie poster"`.
- Treat `<theme>` as style guidance only. It must not affect PR readiness,
BM Bossbot review, status checks, or merge behavior.
## How To Use
Ask Codex to use the skill from a feature branch:
```text
$pr-create
$pr-create "Italian movie poster"
$pr-create "80's action movies"
```
Use the plain form when you only want the PR workflow. Pass a theme when you
want the non-gating image to lean toward a particular visual direction. The
theme can be specific ("Rembrandt-inspired approval scene") or broad ("let the
model choose from BM categories").
## What Happens
1. Codex checks the branch, local verification, GitHub auth, commit sign-offs,
and semantic PR title shape.
2. Codex pushes the branch, creates or reuses the PR, and adds the optional
`BM_INFOGRAPHIC_THEME` block when a theme was supplied.
3. BM Bossbot runs from trusted base code, reviews sanitized PR metadata and
diff context, and sets the required `BM Bossbot Approval` status for the
exact head SHA.
4. If approval succeeds, BM Bossbot may publish a non-gating image block and a
provenance block:
```markdown
<!-- BM_INFOGRAPHIC_PROVENANCE:start -->
...
<!-- BM_INFOGRAPHIC_PROVENANCE:end -->
```
The provenance records the image mode, theme source, selected visual
direction, and image settings. It is for review/debugging context only.
5. Codex reports the PR URL, head SHA, checks watched, verification run, and BM
Bossbot verdict.
The skill never merges, never enables auto-merge, and never treats the image or
provenance block as a gate. The only required merge signal is the
`BM Bossbot Approval` status on the current PR head SHA.
## Preflight
1. Confirm the repo and branch:
- `git status --short --branch`
- stop if detached or on `main`
- keep unrelated user changes intact
2. Confirm GitHub access:
- `gh auth status`
- `gh repo view --json nameWithOwner,defaultBranchRef,url`
3. Check PR readiness:
- commits are signed off with `git commit -s`
- title uses the repo semantic format
- local verification appropriate to the change has run
## Create Or Reuse
1. Push the branch:
- `git push -u origin HEAD`
2. Check for an existing PR:
- `gh pr view --json number,url,headRefOid,mergeStateStatus,statusCheckRollup`
3. If no PR exists, create one:
- `gh pr create --fill`
- adjust the title if it does not satisfy the semantic PR title workflow
4. If `<theme>` is provided, add or update this managed block in the PR body:
```markdown
<!-- BM_INFOGRAPHIC_THEME:start -->
<theme>
<!-- BM_INFOGRAPHIC_THEME:end -->
```
Keep the rest of the PR body intact. The theme is non-gating image guidance
only.
5. Do not merge. Do not enable auto-merge.
## Watch The Gate
1. Trigger or wait for `.github/workflows/bm-bossbot.yml`.
2. Watch the required commit status named `BM Bossbot Approval`.
3. Treat approval as valid only when it is green for the current `headRefOid`.
4. If the branch changes after approval, wait for BM Bossbot to review the new
head SHA.
5. If BM Bossbot fails or requests changes, use `$fix-pr-issues`.
## Report
Return the PR URL, current head SHA, checks watched, verification run, and the
BM Bossbot verdict. Include the image `<theme>` if one was supplied. Be
explicit when any check is still pending.
@@ -1,7 +0,0 @@
interface:
display_name: "PR Create"
short_description: "Create PRs and wait for BM Bossbot"
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#2563EB"
default_prompt: "Use $pr-create to create or update this Basic Memory PR and wait for BM Bossbot Approval."
-5
View File
@@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24" fill="none" stroke="#111827" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 12h4l2-6 4 12 2-6h6"/>
<path d="M4 20h16"/>
</svg>

Before

Width:  |  Height:  |  Size: 249 B

-29
View File
@@ -1,29 +0,0 @@
{
"name": "basicmachines-co",
"owner": {
"name": "Basic Machines",
"email": "hello@basicmachines.co"
},
"metadata": {
"description": "Official Basic Memory plugins from the canonical basic-memory repository",
"version": "0.22.0"
},
"plugins": [
{
"name": "basic-memory",
"source": "./plugins/claude-code",
"description": "The bridge between Claude's working memory and Basic Memory's durable knowledge graph \u2014 session briefings, pre-compaction checkpoints, and capture reflexes",
"version": "0.22.0",
"author": {
"name": "Basic Machines"
},
"keywords": [
"memory",
"knowledge",
"mcp",
"specs",
"context"
]
}
]
}
-96
View File
@@ -1,96 +0,0 @@
# /beta - Create Beta Release
Create a new beta release using the automated justfile target with quality checks and tagging.
## Usage
```
/beta <version>
```
**Parameters:**
- `version` (required): Beta version like `v0.13.2b1` or `v0.13.2rc1`
## Implementation
You are an expert release manager for the Basic Memory project. When the user runs `/beta`, execute the following steps:
### Step 1: Pre-flight Validation
1. Verify version format matches `v\d+\.\d+\.\d+(b\d+|rc\d+)` pattern
2. Check current git status for uncommitted changes
3. Verify we're on the `main` branch
4. Confirm no existing tag with this version
### Step 2: Use Justfile Automation
Execute the automated beta release process:
```bash
just beta <version>
```
The justfile target handles:
- ✅ Beta version format validation (supports b1, b2, rc1, etc.)
- ✅ Git status and branch checks
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
- ✅ Version update across all consolidated manifests via `just set-version` (Python
package + Claude Code plugin/marketplaces + Codex plugin + Hermes + OpenClaw)
- ✅ Automatic commit with proper message
- ✅ Tag creation and pushing to GitHub
- ✅ Beta release workflow trigger
### Step 3: Monitor Beta Release
1. Check GitHub Actions workflow starts successfully
2. Monitor workflow at: https://github.com/basicmachines-co/basic-memory/actions
3. Verify PyPI pre-release publication
4. Test beta installation: `uv tool install basic-memory --pre`
### Step 4: Beta Testing Instructions
Provide users with beta testing instructions:
```bash
# Install/upgrade to beta
uv tool install basic-memory --pre
# Or upgrade existing installation
uv tool upgrade basic-memory --prerelease=allow
```
## Version Guidelines
- **First beta**: `v0.13.2b1`
- **Subsequent betas**: `v0.13.2b2`, `v0.13.2b3`, etc.
- **Release candidates**: `v0.13.2rc1`, `v0.13.2rc2`, etc.
- **Final release**: `v0.13.2` (use `/release` command)
## Error Handling
- If `just beta` fails, examine the error output for specific issues
- If quality checks fail, fix issues and retry
- If version format is invalid, correct and retry
- If tag already exists, increment version number
## Success Output
```
✅ Beta Release v0.13.2b1 Created Successfully!
🏷️ Tag: v0.13.2b1
🚀 GitHub Actions: Running
📦 PyPI: Will be available in ~5 minutes as pre-release
Install/test with:
uv tool install basic-memory --pre
Monitor release: https://github.com/basicmachines-co/basic-memory/actions
```
## Beta Testing Workflow
1. **Create beta**: Use `/beta v0.13.2b1`
2. **Test features**: Install and validate new functionality
3. **Fix issues**: Address bugs found during testing
4. **Iterate**: Create `v0.13.2b2` if needed
5. **Release candidate**: Create `v0.13.2rc1` when stable
6. **Final release**: Use `/release v0.13.2` when ready
## Context
- Beta releases are pre-releases for testing new features
- Automatically published to PyPI with pre-release flag
- Uses the automated justfile target for consistency
- Version is automatically updated across all consolidated manifests via `just set-version`
- Ideal for validating changes before stable release
- Supports both beta (b1, b2) and release candidate (rc1, rc2) versions
-160
View File
@@ -1,160 +0,0 @@
# /changelog - Generate or Update Changelog Entry
Analyze commits and generate formatted changelog entry for a version.
## Usage
```
/changelog <version> [type]
```
**Parameters:**
- `version` (required): Version like `v0.14.0` or `v0.14.0b1`
- `type` (optional): `beta`, `rc`, or `stable` (default: `stable`)
## Implementation
You are an expert technical writer for the Basic Memory project. When the user runs `/changelog`, execute the following steps:
### Step 1: Version Analysis
1. **Determine Commit Range**
```bash
# Find last release tag
git tag -l "v*" --sort=-version:refname | grep -v "b\|rc" | head -1
# Get commits since last release
git log --oneline ${last_tag}..HEAD
```
2. **Parse Conventional Commits**
- Extract feat: (features)
- Extract fix: (bug fixes)
- Extract BREAKING CHANGE: (breaking changes)
- Extract chore:, docs:, test: (other improvements)
### Step 2: Categorize Changes
1. **Features (feat:)**
- New MCP tools
- New CLI commands
- New API endpoints
- Major functionality additions
2. **Bug Fixes (fix:)**
- User-facing bug fixes
- Critical issues resolved
- Performance improvements
- Security fixes
3. **Technical Improvements**
- Test coverage improvements
- Code quality enhancements
- Dependency updates
- Documentation updates
4. **Breaking Changes**
- API changes
- Configuration changes
- Behavior changes
- Migration requirements
### Step 3: Generate Changelog Entry
Create formatted entry following existing CHANGELOG.md style:
Example:
```markdown
## <version> (<date>)
### Features
- **Multi-Project Management System** - Switch between projects instantly during conversations
([`993e88a`](https://github.com/basicmachines-co/basic-memory/commit/993e88a))
- Instant project switching with session context
- Project-specific operations and isolation
- Project discovery and management tools
- **Advanced Note Editing** - Incremental editing with append, prepend, find/replace, and section operations
([`6fc3904`](https://github.com/basicmachines-co/basic-memory/commit/6fc3904))
- `edit_note` tool with multiple operation types
- Smart frontmatter-aware editing
- Validation and error handling
### Bug Fixes
- **#118**: Fix YAML tag formatting to follow standard specification
([`2dc7e27`](https://github.com/basicmachines-co/basic-memory/commit/2dc7e27))
- **#110**: Make --project flag work consistently across CLI commands
([`02dd91a`](https://github.com/basicmachines-co/basic-memory/commit/02dd91a))
### Technical Improvements
- **Comprehensive Testing** - 100% test coverage with integration testing
([`468a22f`](https://github.com/basicmachines-co/basic-memory/commit/468a22f))
- MCP integration test suite
- End-to-end testing framework
- Performance and edge case validation
### Breaking Changes
- **Database Migration**: Automatic migration from per-project to unified database.
Data will be re-index from the filesystem, resulting in no data loss.
- **Configuration Changes**: Projects now synced between config.json and database
- **Full Backward Compatibility**: All existing setups continue to work seamlessly
```
### Step 4: Integration
1. **Update CHANGELOG.md**
- Insert new entry at top
- Maintain consistent formatting
- Include commit links and issue references
2. **Validation**
- Check all major changes are captured
- Verify commit links work
- Ensure issue numbers are correct
## Smart Analysis Features
### Automatic Classification
- Detect feature additions from file changes
- Identify bug fixes from commit messages
- Find breaking changes from code analysis
- Extract issue numbers from commit messages
### Content Enhancement
- Add context for technical changes
- Include migration guidance for breaking changes
- Suggest installation/upgrade instructions
- Link to relevant documentation
## Output Format
### For Beta Releases
Example:
```markdown
## v0.13.0b4 (2025-06-03)
### Beta Changes Since v0.13.0b3
- Fix FastMCP API compatibility issues
- Update dependencies to latest versions
- Resolve setuptools import error
### Installation
```bash
uv tool install basic-memory --prerelease=allow
```
### Known Issues
- [List any known issues for beta testing]
```
### For Stable Releases
Full changelog with complete feature list, organized by impact and category.
## Context
- Follows existing CHANGELOG.md format and style
- Uses conventional commit standards
- Includes GitHub commit links for traceability
- Focuses on user-facing changes and value
- Maintains consistency with previous entries
-131
View File
@@ -1,131 +0,0 @@
# /release-check - Pre-flight Release Validation
Comprehensive pre-flight check for release readiness without making any changes.
## Usage
```
/release-check [version]
```
**Parameters:**
- `version` (optional): Version to validate like `v0.13.0`. If not provided, determines from context.
## Implementation
You are an expert QA engineer for the Basic Memory project. When the user runs `/release-check`, execute the following validation steps:
### Step 1: Environment Validation
1. **Git Status Check**
- Verify working directory is clean
- Confirm on `main` branch
- Check if ahead/behind origin
2. **Version Validation**
- Validate version format if provided
- Check for existing tags with same version
- Verify version increments properly from last release
### Step 2: Code Quality Gates
1. **Test Suite Validation**
```bash
just test
```
- All tests must pass
- Check test coverage (target: 95%+)
- Validate no skipped critical tests
2. **Code Quality Checks**
```bash
just lint
just type-check
```
- No linting errors
- No type checking errors
- Code formatting is consistent
### Step 3: Documentation Validation
1. **Changelog Check**
- CHANGELOG.md contains entry for target version
- Entry includes all major features and fixes
- Breaking changes are documented
2. **Documentation Currency**
- README.md reflects current functionality
- CLI reference is up to date
- MCP tools are documented
### Step 4: Dependency Validation
1. **Security Scan**
- No known vulnerabilities in dependencies
- All dependencies are at appropriate versions
- No conflicting dependency versions
2. **Build Validation**
- Package builds successfully
- All required files are included
- No missing dependencies
### Step 5: Issue Tracking Validation
1. **GitHub Issues Check**
- No critical open issues blocking release
- All milestone issues are resolved
- High-priority bugs are fixed
2. **Testing Coverage**
- Integration tests pass
- MCP tool tests pass
- Cross-platform compatibility verified
## Report Format
Generate a comprehensive report:
```
🔍 Release Readiness Check for v0.13.0
✅ PASSED CHECKS:
├── Git status clean
├── On main branch
├── All tests passing (744/744)
├── Test coverage: 98.2%
├── Type checking passed
├── Linting passed
├── CHANGELOG.md updated
└── No critical issues open
⚠️ WARNINGS:
├── 2 medium-priority issues still open
└── Documentation could be updated
❌ BLOCKING ISSUES:
└── None found
🎯 RELEASE READINESS: ✅ READY
Recommended next steps:
1. Address warnings if desired
2. Run `/release v0.13.0` when ready
```
## Validation Criteria
### Must Pass (Blocking)
- [ ] All tests pass
- [ ] No type errors
- [ ] No linting errors
- [ ] Working directory clean
- [ ] On main branch
- [ ] CHANGELOG.md has version entry
- [ ] No critical open issues
### Should Pass (Warnings)
- [ ] Test coverage >95%
- [ ] No medium-priority open issues
- [ ] Documentation up to date
- [ ] No dependency vulnerabilities
## Context
- This is a read-only validation - makes no changes
- Provides confidence before running actual release
- Helps identify issues early in release process
- Can be run multiple times safely
-207
View File
@@ -1,207 +0,0 @@
# /release - Create Stable Release
Create a stable release using the automated justfile target with comprehensive validation.
## Usage
```
/release <version>
```
**Parameters:**
- `version` (required): Release version like `v0.13.2`
## Implementation
You are an expert release manager for the Basic Memory project. When the user runs `/release`, execute the following steps:
### Step 1: Pre-flight Validation
#### Version Check
1. Check current version in `src/basic_memory/__init__.py`
2. Verify new version format matches `v\d+\.\d+\.\d+` pattern
3. Confirm version is higher than current version
#### Git Status
1. Check current git status for uncommitted changes
2. Verify we're on the `main` branch
3. Confirm no existing tag with this version
#### Documentation Validation
1. **Changelog Check**
- CHANGELOG.md contains entry for target version
- Entry includes all major features and fixes
- Breaking changes are documented
### Step 2: Use Justfile Automation
Execute the automated release process:
```bash
just release <version>
```
The justfile target handles:
- ✅ Version format validation
- ✅ Git status and branch checks
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
- ✅ Version update across all consolidated manifests via `just set-version` (Python
package + Claude Code plugin/marketplaces + Codex plugin + Hermes + OpenClaw)
- ✅ Automatic commit with proper message
- ✅ Tag creation and pushing to GitHub
- ✅ Release workflow trigger (automatic on tag push)
The GitHub Actions workflow (`.github/workflows/release.yml`) then:
- ✅ Builds the package using `uv build`
- ✅ Creates GitHub release with auto-generated notes
- ✅ Publishes to PyPI
- ✅ Updates Homebrew formula (stable releases only)
### Step 3: Monitor Release Process
1. Verify tag push triggered the workflow (should start automatically within seconds)
2. Monitor workflow progress at: https://github.com/basicmachines-co/basic-memory/actions
3. Watch for successful completion of both jobs:
- `release` - Builds package and publishes to PyPI
- `homebrew` - Updates Homebrew formula (stable releases only)
4. Check for any workflow failures and investigate logs if needed
### Step 4: Post-Release Validation
#### GitHub Release
1. Verify GitHub release is created at: https://github.com/basicmachines-co/basic-memory/releases/tag/<version>
2. Check that release notes are auto-generated from commits
3. Validate release assets (`.whl` and `.tar.gz` files are attached)
#### PyPI Publication
1. Verify package published at: https://pypi.org/project/basic-memory/<version>/
2. Test installation: `uv tool install basic-memory`
3. Verify installed version: `basic-memory --version`
#### Homebrew Formula (Stable Releases Only)
1. Check formula update at: https://github.com/basicmachines-co/homebrew-basic-memory
2. Verify formula version matches release
3. Test Homebrew installation: `brew install basicmachines-co/basic-memory/basic-memory`
#### MCP Registry Publication
After PyPI release is published, update the MCP registry:
1. **Verify PyPI Release**
- Confirm package is live: https://pypi.org/project/basic-memory/<version>/
- The `server.json` version was auto-updated by `just release`
2. **Publish to MCP Registry**
```bash
cd /Users/drew/code/basic-memory
mcp-publisher publish
```
If not authenticated:
```bash
mcp-publisher login github
# Follow device authentication flow
mcp-publisher publish
```
3. **Verify Publication**
```bash
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=basic-memory"
```
**Note:** The `mcp-publisher` CLI can be installed via Homebrew (`brew install mcp-publisher`) or from GitHub releases.
#### Website Updates
**1. basicmachines.co** (`/Users/drew/code/basicmachines.co`)
- **Goal**: Update version number displayed on the homepage
- **Location**: Search for "Basic Memory v0." in the codebase to find version displays
- **What to update**:
- Hero section heading that shows "Basic Memory v{VERSION}"
- "What's New in v{VERSION}" section heading
- Feature highlights array (look for array of features with title/description)
- **Process**:
1. Pull latest from GitHub: `git pull origin main`
2. Create release branch: `git checkout -b release/v{VERSION}`
3. Search codebase for current version number (e.g., "v0.16.1")
4. Update version numbers to new release version
5. Update feature highlights with 3-5 key features from this release (extract from CHANGELOG.md)
6. Commit changes: `git commit -m "chore: update to v{VERSION}"`
7. Push branch: `git push origin release/v{VERSION}`
- **Deploy**: Follow deployment process for basicmachines.co
**2. docs.basicmemory.com** (`/Users/drew/code/docs.basicmemory.com`)
- **Goal**: Add new release notes section to the latest-releases page
- **File**: `src/pages/latest-releases.mdx`
- **What to do**:
1. Pull latest from GitHub: `git pull origin main`
2. Create release branch: `git checkout -b release/v{VERSION}`
3. Read the existing file to understand the format and structure
4. Read `/Users/drew/code/basic-memory/CHANGELOG.md` to get release content
5. Add new release section **at the top** (after MDX imports, before other releases)
6. Follow the existing pattern:
- Heading: `## [v{VERSION}](github-link) — YYYY-MM-DD`
- Focus statement if applicable
- `<Info>` block with highlights (3-5 key items)
- Sections for Features, Bug Fixes, Breaking Changes, etc.
- Link to full changelog at the end
- Separator `---` between releases
7. Commit changes: `git commit -m "docs: add v{VERSION} release notes"`
8. Push branch: `git push origin release/v{VERSION}`
- **Source content**: Extract and format sections from CHANGELOG.md for this version
- **Deploy**: Follow deployment process for docs.basicmemory.com
**4. Announce Release**
- Post to Discord community if significant changes
- Update social media if major release
- Notify users via appropriate channels
## Pre-conditions Check
Before starting, verify:
- [ ] All beta testing is complete
- [ ] Critical bugs are fixed
- [ ] Breaking changes are documented
- [ ] CHANGELOG.md is updated (if needed)
- [ ] Version number follows semantic versioning
## Error Handling
- If `just release` fails, examine the error output for specific issues
- If quality checks fail, fix issues and retry
- If changelog entry missing, update CHANGELOG.md and commit before retrying
- If GitHub Actions fail, check workflow logs for debugging
## Success Output
```
🎉 Stable Release v0.13.2 Created Successfully!
🏷️ Tag: v0.13.2
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.2
📦 PyPI: https://pypi.org/project/basic-memory/0.13.2/
🍺 Homebrew: https://github.com/basicmachines-co/homebrew-basic-memory
🔌 MCP Registry: https://registry.modelcontextprotocol.io
🚀 GitHub Actions: Completed
Install with pip/uv:
uv tool install basic-memory
Install with Homebrew:
brew install basicmachines-co/basic-memory/basic-memory
Users can now upgrade:
uv tool upgrade basic-memory
brew upgrade basic-memory
```
## Context
- This creates production releases used by end users
- Must pass all quality gates before proceeding
- Uses the automated justfile target for consistency
- Version is automatically updated across **all** consolidated manifests via
`just set-version <version>` (which calls `scripts/update_versions.py`): the
Python package (`__init__.py`, `server.json`) **and** the plugin/agent artifacts
(Claude Code `plugin.json` + root/local marketplaces, Codex `plugin.json`,
Hermes `plugin.yaml` + `__init__.py`, OpenClaw `package.json`). To bump only
the plugin/agent artifacts
out of band, use `just set-packages-version <version>` (preview with
`just set-packages-version-dry-run <version>`).
- Triggers automated GitHub release with changelog
- Package is published to PyPI for `pip` and `uv` users
- Homebrew formula is automatically updated for stable releases
- MCP Registry is updated manually via `mcp-publisher publish`
- Supports multiple installation methods (uv, pip, Homebrew)
-51
View File
@@ -1,51 +0,0 @@
---
allowed-tools: mcp__basic-memory__write_note, mcp__basic-memory__read_note, mcp__basic-memory__search_notes, mcp__basic-memory__edit_note
argument-hint: [create|status|show|review] [spec-name]
description: Manage specifications in our development process
---
## Context
Specifications are managed in the Basic Memory "specs" project. All specs live in a centralized location accessible across all repositories via MCP tools.
See SPEC-1 and SPEC-2 in the "specs" project for the full specification-driven development process.
Available commands:
- `create [name]` - Create new specification
- `status` - Show all spec statuses
- `show [spec-name]` - Read a specific spec
- `review [spec-name]` - Review implementation against spec
## Your task
Execute the spec command: `/spec $ARGUMENTS`
### If command is "create":
1. Get next SPEC number by searching existing specs in "specs" project
2. Create new spec using template from SPEC-2
3. Use mcp__basic-memory__write_note with project="specs"
4. Include standard sections: Why, What, How, How to Evaluate
### If command is "status":
1. Use mcp__basic-memory__search_notes with project="specs"
2. Display table with spec number, title, and progress
3. Show completion status from checkboxes in content
### If command is "show":
1. Use mcp__basic-memory__read_note with project="specs"
2. Display the full spec content
### If command is "review":
1. Read the specified spec and its "How to Evaluate" section
2. Review current implementation against success criteria with careful evaluation of:
- **Functional completeness** - All specified features working
- **Test coverage analysis** - Actual test files and coverage percentage
- Count existing test files vs required components/APIs/composables
- Verify unit tests, integration tests, and end-to-end tests
- Check for missing test categories (component, API, workflow)
- **Code quality metrics** - TypeScript compilation, linting, performance
- **Architecture compliance** - Component isolation, state management patterns
- **Documentation completeness** - Implementation matches specification
3. Provide honest, accurate assessment - do not overstate completeness
4. Document findings and update spec with review results using mcp__basic-memory__edit_note
5. If gaps found, clearly identify what still needs to be implemented/tested
-622
View File
@@ -1,622 +0,0 @@
# /project:test-live - Live Basic Memory Testing Suite
Execute comprehensive real-world testing of Basic Memory using the installed version.
All test results are recorded as notes in a dedicated test project.
## Usage
```
/project:test-live [phase]
```
**Parameters:**
- `phase` (optional): Specific test phase to run (`recent`, `core`, `features`, `edge`, `workflows`, `stress`, or `all`)
- `recent` - Focus on recent changes and new features (recommended for regular testing)
- `core` - Essential tools only (Tier 1: write_note, read_note, search_notes, edit_note, list_memory_projects, recent_activity)
- `features` - Core + important workflows (Tier 1 + Tier 2)
- `all` - Comprehensive testing of all tools and scenarios
## Implementation
You are an expert QA engineer conducting live testing of Basic Memory.
When the user runs `/project:test-live`, execute comprehensive test plan:
## Tool Testing Priority
### **Tier 1: Critical Core (Always Test)**
1. **write_note** - Foundation of all knowledge creation
2. **read_note** - Primary knowledge retrieval mechanism
3. **search_notes** - Essential for finding information
4. **edit_note** - Core content modification capability
5. **list_memory_projects** - Project discovery and session guidance
6. **recent_activity** - Project discovery mode and activity analysis
### **Tier 2: Important Workflows (Usually Test)**
7. **build_context** - Conversation continuity via memory:// URLs
8. **create_memory_project** - Essential for project setup
9. **move_note** - Knowledge organization
10. **sync_status** - Understanding system state
11. **delete_project** - Project lifecycle management
### **Tier 3: Enhanced Functionality (Sometimes Test)**
12. **view_note** - Claude Desktop artifact display
13. **read_content** - Raw content access
14. **delete_note** - Content removal
15. **list_directory** - File system exploration
16. **edit_note** (advanced modes) - Complex find/replace operations
### **Tier 4: Specialized (Rarely Test)**
17. **canvas** - Obsidian visualization (specialized use case)
18. **MCP Prompts** - Enhanced UX tools (ai_assistant_guide, continue_conversation)
## Stateless Architecture Testing
### **Project Discovery Workflow (CRITICAL)**
Test the new stateless project selection flow:
1. **Initial Discovery**
- Call `list_memory_projects()` without knowing which project to use
- Verify clear session guidance appears: "Next: Ask which project to use"
- Confirm removal of CLI-specific references
2. **Activity-Based Discovery**
- Call `recent_activity()` without project parameter (discovery mode)
- Verify intelligent project suggestions based on activity
- Test guidance: "Should I use [most-active-project] for this task?"
3. **Session Tracking Validation**
- Verify all tool responses include `[Session: Using project 'name']`
- Confirm guidance reminds about session-wide project tracking
4. **Single Project Constraint Mode**
- Test MCP server with `--project` parameter
- Verify all operations constrained to specified project
- Test project override behavior in constrained mode
### **Explicit Project Parameters (CRITICAL)**
All tools must require explicit project parameters:
1. **Parameter Validation**
- Test all Tier 1 tools require `project` parameter
- Verify clear error messages for missing project
- Test invalid project name handling
2. **No Session State Dependencies**
- Confirm no tool relies on "current project" concept
- Test rapid project switching within conversation
- Verify each call is truly independent
### Pre-Test Setup
1. **Environment Verification**
- Verify basic-memory is installed and accessible via MCP
- Check version and confirm it's the expected release
- Test MCP connection and tool availability
2. **Recent Changes Analysis** (if phase includes 'recent' or 'all')
- Run `git log --oneline -20` to examine recent commits
- Identify new features, bug fixes, and enhancements
- Generate targeted test scenarios for recent changes
- Prioritize regression testing for recently fixed issues
3. **Test Project Creation**
Run the bash `date` command to get the current date/time.
```
Create project: "basic-memory-testing-[timestamp]"
Location: ~/basic-memory-testing-[timestamp]
Purpose: Record all test observations and results
```
Make sure to use the newly created project for all subsequent test operations by specifying it in the `project` parameter of each tool call.
4. **Baseline Documentation**
Create initial test session note with:
- Test environment details
- Version being tested
- Recent changes identified (if applicable)
- Test objectives and scope
- Start timestamp
### Phase 0: Recent Changes Validation (if 'recent' or 'all' phase)
Based on recent commit analysis, create targeted test scenarios:
**Recent Changes Test Protocol:**
1. **Feature Addition Tests** - For each new feature identified:
- Test basic functionality
- Test integration with existing tools
- Verify documentation accuracy
- Test edge cases and error handling
2. **Bug Fix Regression Tests** - For each recent fix:
- Recreate the original problem scenario
- Verify the fix works as expected
- Test related functionality isn't broken
- Document the verification in test notes
3. **Performance/Enhancement Validation** - For optimizations:
- Establish baseline timing
- Compare with expected improvements
- Test under various load conditions
- Document performance observations
**Example Recent Changes (Update based on actual git log):**
- Watch Service Restart (#156): Test project creation → file modification → automatic restart
- Cross-Project Moves (#161): Test move_note with cross-project detection
- Docker Environment Support (#174): Test BASIC_MEMORY_HOME behavior
- MCP Server Logging (#164): Verify log level configurations
### Phase 1: Core Functionality Validation (Tier 1 Tools)
Test essential MCP tools that form the foundation of Basic Memory:
**1. write_note Tests (Critical):**
- ✅ Basic note creation with frontmatter
- ✅ Special characters and Unicode in titles
- ✅ Various content types (lists, headings, code blocks)
- ✅ Empty notes and minimal content edge cases
- ⚠️ Error handling for invalid parameters
**2. read_note Tests (Critical):**
- ✅ Read by title, permalink, memory:// URLs
- ✅ Non-existent notes (error handling)
- ✅ Notes with complex markdown formatting
- ⚠️ Performance with large notes (>10MB)
**3. search_notes Tests (Critical):**
- ✅ Simple text queries across content
- ✅ Tag-based searches with multiple tags
- ✅ Boolean operators (AND, OR, NOT)
- ✅ Empty/no results scenarios
- ⚠️ Performance with 100+ notes
**4. edit_note Tests (Critical):**
- ✅ Append operations preserving frontmatter
- ✅ Prepend operations
- ✅ Find/replace with validation
- ✅ Section replacement under headers
- ⚠️ Error scenarios (invalid operations)
**5. list_memory_projects Tests (Critical):**
- ✅ Display all projects with clear session guidance
- ✅ Project discovery workflow prompts
- ✅ Removal of CLI-specific references
- ✅ Empty project list handling
- ✅ Single project constraint mode display
**6. recent_activity Tests (Critical - Discovery Mode):**
- ✅ Discovery mode without project parameter
- ✅ Intelligent project suggestions based on activity
- ✅ Guidance prompts for project selection
- ✅ Session tracking reminders in responses
- ⚠️ Performance with multiple projects
### Phase 2: Important Workflows (Tier 2 Tools)
**7. build_context Tests (Important):**
- ✅ Different depth levels (1, 2, 3+)
- ✅ Various timeframes for context
- ✅ memory:// URL navigation
- ⚠️ Performance with complex relation graphs
**8. create_memory_project Tests (Important):**
- ✅ Create projects dynamically
- ✅ Set default during creation
- ✅ Path validation and creation
- ⚠️ Invalid paths and names
- ✅ Integration with existing projects
**9. move_note Tests (Important):**
- ✅ Move within same project
- ✅ Cross-project moves with detection (#161)
- ✅ Automatic folder creation
- ✅ Database consistency validation
- ⚠️ Special characters in paths
**10. sync_status Tests (Important):**
- ✅ Background operation monitoring
- ✅ File synchronization status
- ✅ Project sync state reporting
- ⚠️ Error state handling
### Phase 3: Enhanced Functionality (Tier 3 Tools)
**11. view_note Tests (Enhanced):**
- ✅ Claude Desktop artifact display
- ✅ Title extraction from frontmatter
- ✅ Unicode and emoji content rendering
- ⚠️ Error handling for non-existent notes
**12. read_content Tests (Enhanced):**
- ✅ Raw file content access
- ✅ Binary file handling
- ✅ Image file reading
- ⚠️ Large file performance
**13. delete_note Tests (Enhanced):**
- ✅ Single note deletion
- ✅ Database consistency after deletion
- ⚠️ Non-existent note handling
- ✅ Confirmation of successful deletion
**14. list_directory Tests (Enhanced):**
- ✅ Directory content listing
- ✅ Depth control and filtering
- ✅ File name globbing
- ⚠️ Empty directory handling
**15. delete_project Tests (Enhanced):**
- ✅ Project removal from config
- ✅ Database cleanup
- ⚠️ Default project protection
- ⚠️ Non-existent project handling
### Phase 4: Edge Case Exploration
**Boundary Testing:**
- Very long titles and content (stress limits)
- Empty projects and notes
- Unicode, emojis, special symbols
- Deeply nested folder structures
- Circular relations and self-references
- Maximum relation depths
**Error Scenarios:**
- Invalid memory:// URLs
- Missing files referenced in database
- Invalid project names and paths
- Malformed note structures
- Concurrent operation conflicts
**Performance Testing:**
- Create 100+ notes rapidly
- Complex search queries
- Deep relation chains (5+ levels)
- Rapid successive operations
- Memory usage monitoring
### Phase 5: Real-World Workflow Scenarios
**Meeting Notes Pipeline:**
1. Create meeting notes with action items
2. Extract action items using edit_note
3. Build relations to project documents
4. Update progress incrementally
5. Search and track completion
**Research Knowledge Building:**
1. Create research topic hierarchy
2. Build complex relation networks
3. Add incremental findings over time
4. Search for connections and patterns
5. Reorganize as knowledge evolves
**Multi-Project Workflow:**
1. Technical documentation project
2. Personal recipe collection project
3. Learning/course notes project
4. Specify different projects for different operations
5. Cross-reference related concepts
**Content Evolution:**
1. Start with basic notes
2. Enhance with relations and observations
3. Reorganize file structure using moves
4. Update content with edit operations
5. Validate knowledge graph integrity
### Phase 6: Specialized Tools Testing (Tier 4)
**16. canvas Tests (Specialized):**
- ✅ JSON Canvas generation
- ✅ Node and edge creation
- ✅ Obsidian compatibility
- ⚠️ Complex graph handling
**17. MCP Prompts Tests (Specialized):**
- ✅ ai_assistant_guide output
- ✅ continue_conversation functionality
- ✅ Formatted search results
- ✅ Enhanced activity reports
### Phase 7: Integration & File Watching Tests
**File System Integration:**
- ✅ Watch service behavior with file changes
- ✅ Project creation → watch restart (#156)
- ✅ Multi-project synchronization
- ⚠️ MCP→API→DB→File stack validation
**Real Integration Testing:**
- ✅ End-to-end file watching vs manual operations
- ✅ Cross-session persistence
- ✅ Database consistency across operations
- ⚠️ Performance under real file system changes
### Phase 8: Creative Stress Testing
**Creative Exploration:**
- Rapid project creation/switching patterns
- Unusual but valid markdown structures
- Creative observation categories
- Novel relation types and patterns
- Unexpected tool combinations
**Stress Scenarios:**
- Bulk operations (many notes quickly)
- Complex nested moves and edits
- Deep context building
- Complex boolean search expressions
- Resource constraint testing
## Test Execution Guidelines
### Quick Testing (core/features phases)
- Focus on Tier 1 tools (core) or Tier 1+2 (features)
- Test essential functionality and common edge cases
- Record critical issues immediately
- Complete in 15-20 minutes
### Comprehensive Testing (all phase)
- Cover all tiers systematically
- Include specialized tools and stress testing
- Document performance baselines
- Complete in 45-60 minutes
### Recent Changes Focus (recent phase)
- Analyze git log for recent commits
- Generate targeted test scenarios
- Focus on regression testing for fixes
- Validate new features thoroughly
## Test Observation Format
Record ALL observations immediately as Basic Memory notes:
```markdown
---
title: Test Session [Phase] YYYY-MM-DD HH:MM
tags: [testing, v0.13.0, live-testing, [phase]]
permalink: test-session-[phase]-[timestamp]
---
# Test Session [Phase] - [Date/Time]
## Environment
- Basic Memory version: [version]
- MCP connection: [status]
- Test project: [name]
- Phase focus: [description]
## Test Results
### ✅ Successful Operations
- [timestamp] ✅ write_note: Created note with emoji title 📝 #tier1 #functionality
- [timestamp] ✅ search_notes: Boolean query returned 23 results in 0.4s #tier1 #performance
- [timestamp] ✅ edit_note: Append operation preserved frontmatter #tier1 #reliability
### ⚠️ Issues Discovered
- [timestamp] ⚠️ move_note: Slow with deep folder paths (2.1s) #tier2 #performance
- [timestamp] 🚨 search_notes: Unicode query returned unexpected results #tier1 #bug #critical
- [timestamp] ⚠️ build_context: Context lost for memory:// URLs #tier2 #issue
### 🚀 Enhancements Identified
- edit_note could benefit from preview mode #ux-improvement
- search_notes needs fuzzy matching for typos #feature-idea
- move_note could auto-suggest folder creation #usability
### 📊 Performance Metrics
- Average write_note time: 0.3s
- Search with 100+ notes: 0.6s
- Project parameter overhead: <0.1s
- Memory usage: [observed levels]
## Relations
- tests [[Basic Memory v0.13.0]]
- part_of [[Live Testing Suite]]
- found_issues [[Bug Report: Unicode Search]]
- discovered [[Performance Optimization Opportunities]]
```
## Quality Assessment Areas
**User Experience & Usability:**
- Tool instruction clarity and examples
- Error message actionability
- Response time acceptability
- Tool consistency and discoverability
- Learning curve and intuitiveness
**System Behavior:**
- Stateless operation independence
- memory:// URL navigation reliability
- Multi-step workflow cohesion
- Edge case graceful handling
- Recovery from user errors
**Documentation Alignment:**
- Tool output clarity and helpfulness
- Behavior vs. documentation accuracy
- Example validity and usefulness
- Real-world vs. documented workflows
**Mental Model Validation:**
- Natural user expectation alignment
- Surprising behavior identification
- Mistake recovery ease
- Knowledge graph concept naturalness
**Performance & Reliability:**
- Operation completion times
- Consistency across sessions
- Scaling behavior with growth
- Unexpected slowness identification
## Error Documentation Protocol
For each error discovered:
1. **Immediate Recording**
- Create dedicated error note
- Include exact reproduction steps
- Capture error messages verbatim
- Note system state when error occurred
2. **Error Note Format**
```markdown
---
title: Bug Report - [Short Description]
tags: [bug, testing, v0.13.0, [severity]]
---
# Bug Report: [Description]
## Reproduction Steps
1. [Exact steps to reproduce]
2. [Include all parameters used]
3. [Note any special conditions]
## Expected Behavior
[What should have happened]
## Actual Behavior
[What actually happened]
## Error Messages
```
[Exact error text]
```
## Environment
- Version: [version]
- Project: [name]
- Timestamp: [when]
## Severity
- [ ] Critical (blocks major functionality)
- [ ] High (impacts user experience)
- [ ] Medium (workaround available)
- [ ] Low (minor inconvenience)
## Relations
- discovered_during [[Test Session [Phase]]]
- affects [[Feature Name]]
```
## Success Metrics Tracking
**Quantitative Measures:**
- Test scenario completion rate
- Bug discovery count with severity
- Performance benchmark establishment
- Tool coverage completeness
**Qualitative Measures:**
- Conversation flow naturalness
- Knowledge graph quality
- User experience insights
- System reliability assessment
## Test Execution Flow
1. **Setup Phase** (5 minutes)
- Verify environment and create test project
- Record baseline system state
- Establish performance benchmarks
2. **Core Testing** (15-20 minutes per phase)
- Execute test scenarios systematically
- Record observations immediately
- Note timestamps for performance tracking
- Explore variations when interesting behaviors occur
3. **Documentation** (5 minutes per phase)
- Create phase summary note
- Link related test observations
- Update running issues list
- Record enhancement ideas
4. **Analysis Phase** (10 minutes)
- Review all observations across phases
- Identify patterns and trends
- Create comprehensive summary report
- Generate development recommendations
## Testing Success Criteria
### Core Testing (Tier 1) - Must Pass
- All 6 critical tools function correctly
- No critical bugs in essential workflows
- Acceptable performance for basic operations
- Error handling works as expected
### Feature Testing (Tier 1+2) - Should Pass
- All 11 core + important tools function
- Workflow scenarios complete successfully
- Performance meets baseline expectations
- Integration points work correctly
### Comprehensive Testing (All Tiers) - Complete Coverage
- All tools tested across all scenarios
- Edge cases and stress testing completed
- Performance baselines established
- Full documentation of issues and enhancements
## Expected Outcomes
**System Validation:**
- Feature verification prioritized by tier importance
- Recent changes validated for regression
- Performance baseline establishment
- Bug identification with severity assessment
**Knowledge Base Creation:**
- Prioritized testing documentation
- Real usage examples for user guides
- Recent changes validation records
- Performance insights for optimization
**Development Insights:**
- Tier-based bug priority list
- Recent changes impact assessment
- Enhancement ideas from real usage
- User experience improvement areas
## Post-Test Deliverables
1. **Test Summary Note**
- Overall results and findings
- Critical issues requiring immediate attention
- Enhancement opportunities discovered
- System readiness assessment
2. **Bug Report Collection**
- All discovered issues with reproduction steps
- Severity and impact assessments
- Suggested fixes where applicable
3. **Performance Baseline**
- Timing data for all operations
- Scaling behavior observations
- Resource usage patterns
4. **UX Improvement Recommendations**
- Usability enhancement suggestions
- Documentation improvement areas
- Tool design optimization ideas
5. **Updated TESTING.md**
- Incorporate new test scenarios discovered
- Update based on real execution experience
- Add performance benchmarks and targets
## Context
- Uses real installed basic-memory version
- Tests complete MCP→API→DB→File stack
- Creates living documentation in Basic Memory itself
- Follows integration over isolation philosophy
- Prioritizes testing by tool importance and usage frequency
- Adapts to recent development changes dynamically
- Focuses on real usage patterns over checklist validation
- Generates actionable insights prioritized by impact
-21
View File
@@ -1,21 +0,0 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"env": {
"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "1",
"CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY": "1",
"CLAUDE_CODE_NO_FLICKER": "1",
"CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1"
},
"permissions": {
"allow": [
"Bash(just fast-check)",
"Bash(just check)",
"Bash(just fix)",
"Bash(just typecheck)",
"Bash(just lint)",
"Bash(just test)"
],
"deny": []
},
"enableAllProjectMcpServers": true
}
-1
View File
@@ -1 +0,0 @@
../../.agents/skills/adversarial-review
-1
View File
@@ -1 +0,0 @@
../../.agents/skills/basic-machines-review
-1
View File
@@ -1 +0,0 @@
../../.agents/skills/instrumentation
-60
View File
@@ -1,60 +0,0 @@
# Git files
.git/
.gitignore
.gitattributes
# Development files
.vscode/
.idea/
*.swp
*.swo
*~
# Testing files
tests/
test-int/
.pytest_cache/
.coverage
htmlcov/
# Build artifacts
build/
dist/
*.egg-info/
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
# Virtual environments (uv creates these during build)
.venv/
venv/
.env
# CI/CD files
.github/
# Documentation (keep README.md and pyproject.toml)
docs/
CHANGELOG.md
CLAUDE.md
CONTRIBUTING.md
# Example files not needed for runtime
examples/
# Local development files
.basic-memory/
*.db
*.sqlite3
# OS files
.DS_Store
Thumbs.db
# Temporary files
tmp/
temp/
*.tmp
*.log
-28
View File
@@ -1,28 +0,0 @@
# Basic Memory Environment Variables Example
# Copy this file to .env and customize as needed
# Note: .env files are gitignored and should never be committed
# ============================================================================
# PostgreSQL Test Database Configuration
# ============================================================================
# These variables allow you to override the default test database credentials
# Default values match docker-compose-postgres.yml for local development
#
# Only needed if you want to use different credentials or a remote test database
# By default, tests use: postgresql://basic_memory_user:dev_password@localhost:5433/basic_memory_test
# Full PostgreSQL test database URL (used by tests and migrations)
# POSTGRES_TEST_URL=postgresql+asyncpg://basic_memory_user:dev_password@localhost:5433/basic_memory_test
# Individual components (used by justfile postgres-reset command)
# POSTGRES_USER=basic_memory_user
# POSTGRES_TEST_DB=basic_memory_test
# ============================================================================
# Production Database Configuration
# ============================================================================
# For production use, set these in your deployment environment
# DO NOT use the test credentials above in production!
# BASIC_MEMORY_DATABASE_BACKEND=postgres # or "sqlite"
# BASIC_MEMORY_DATABASE_URL=postgresql+asyncpg://user:password@host:port/database
-25
View File
@@ -1,25 +0,0 @@
# Auto BM Soul
Write project updates for humans who will return later trying to understand what happened.
## Voice
- Clear, direct, warm, and technically honest.
- Prefer concrete observations over generic praise.
- It is okay to say when code is messy, risky, clever, boring, or satisfying.
- Keep personality in service of memory, not performance.
## Do
- Tell the story.
- Name the tradeoffs.
- Call out sharp edges.
- Notice good simplifications.
- Let the note have taste and a little life when the evidence supports it.
## Do Not
- Do not invent intent, impact, tests, or drama.
- Dunk on people.
- Turn the note into marketing copy.
- Hide uncertainty behind confident prose.
-7
View File
@@ -1,7 +0,0 @@
project: dev
workspace: basic-memory-7020de4e925843c68c9056c60d101d9e
deploy_workflows:
- Deploy Production
production_environments:
- production
note_folder: project-updates/github/{owner}/{repo}
-64
View File
@@ -1,64 +0,0 @@
# Memory CI Capture
You turn GitHub delivery context into a durable project update for Basic Memory.
GitHub records the mechanics. Basic Memory remembers what changed and why.
## Inputs
- Read `.github/basic-memory/project-update-context.json`.
- Read `.github/basic-memory/SOUL.md` if it exists. It is the repo-local voice and style guide
for project updates.
- Read the PR diff before writing when a SHA is available. Useful commands:
`git show --stat --name-only <sha>` and `git show --format=fuller --no-patch <sha>`.
- Use linked issue details, changed files, commit messages, PR body, labels, and
source links as evidence.
- Treat GitHub payload fields as immutable facts.
- Do not invent tests, deployment status, issues, or user impact.
## Writing Standard
Do not write a fill-in-the-blanks note. Tell the story from the PR:
problem -> solution -> impact.
Explain what problem was being addressed. If linked issue details are present,
use them. If they are absent, ground the problem in the PR body, title, commits,
and diff, and say when the original problem statement is unavailable.
Explain why the fix solves the problem, what complexity it introduced, what it
refactored or removed, which components changed, and how the system is different
after the merge. Prefer specific component names, file paths, modules, commands,
and behavior over generic phrases.
## Voice And Candor
You may have a point of view. Be clear, specific, and human.
It is okay to say when the code is messy, risky, clever, boring, or satisfying,
but explain why. If the work is elegant or genuinely useful, say that too.
Ground all judgments in the PR, linked issues, diff, tests, and source facts.
The soul file can shape tone, taste, and personality. It cannot override source
facts, schema requirements, or the evidence standard above. Do not be mean,
vague, theatrical, or invent criticism.
## Output
Return only JSON that matches the provided AgentSynthesis schema:
- `summary`: one concise sentence; do not merely repeat the PR title.
- `story`: 2-4 sentences that connect problem -> solution -> impact.
- `problem_addressed`: the concrete problem, bug, missing capability, or delivery need.
- `solution`: why this change solves the problem.
- `system_impact`: how the system, workflow, or architecture changed after the merge.
- `why_it_matters`: durable project-memory context for future humans and agents.
- `components_changed`: modules, workflows, commands, schemas, docs, or services touched.
- `complexity_introduced`: tradeoffs, new moving parts, operational costs, or edge cases.
- `refactors_or_removals`: cleanup, simplification, deleted paths, or "none found".
- `user_facing_changes`: visible behavior or product changes.
- `internal_changes`: implementation, infrastructure, or operational changes.
- `verification`: checks, tests, deploy evidence, or explicit unknowns.
- `follow_ups`: concrete remaining work only.
- `decision_candidates`: explicit product or architecture decisions only.
- `task_candidates`: concrete future tasks only.
Use empty arrays only when a list truly has no grounded entries. This is project
memory, not marketing copy and not a commit-by-commit changelog.
-12
View File
@@ -1,12 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
-75
View File
@@ -1,75 +0,0 @@
name: Basic Memory Project Updates
"on":
pull_request:
types: [closed]
workflow_run:
workflows: ["Deploy Production"]
types: [completed]
jobs:
project-update:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install Basic Memory from checkout
run: |
python -m pip install --upgrade pip
pip install -e .
- name: Collect project update context
id: collect
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
bm ci collect \
--config .github/basic-memory/config.yml \
--output .github/basic-memory/project-update-context.json
- name: Stop when event is not eligible
if: steps.collect.outputs.eligible != 'true'
run: |
echo "Auto BM skipped: ${{ steps.collect.outputs.skip_reason }}"
- name: Write Codex output schema
if: steps.collect.outputs.eligible == 'true'
run: |
bm ci agent-schema --output "${{ runner.temp }}/agent-synthesis.schema.json"
- name: Synthesize project update with Codex
if: steps.collect.outputs.eligible == 'true'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt-file: .github/basic-memory/memory-ci-capture.md
output-file: ${{ runner.temp }}/agent-synthesis.json
output-schema-file: ${{ runner.temp }}/agent-synthesis.schema.json
sandbox: read-only
safety-strategy: drop-sudo
- name: Publish project update
if: steps.collect.outputs.eligible == 'true'
env:
BASIC_MEMORY_CLOUD_API_KEY: ${{ secrets.BASIC_MEMORY_API_KEY }}
BASIC_MEMORY_CI_CLOUD_HOST: ${{ vars.BASIC_MEMORY_CLOUD_HOST }}
run: |
if [ -n "$BASIC_MEMORY_CI_CLOUD_HOST" ]; then
export BASIC_MEMORY_CLOUD_HOST="$BASIC_MEMORY_CI_CLOUD_HOST"
fi
bm ci publish \
--cloud \
--config .github/basic-memory/config.yml \
--context .github/basic-memory/project-update-context.json \
--synthesis "${{ runner.temp }}/agent-synthesis.json"
-85
View File
@@ -1,85 +0,0 @@
name: Claude Code Review
"on":
workflow_dispatch:
inputs:
pr_number:
description: Pull request number to review manually
required: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
claude-review:
if: inputs.pr_number != ''
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
track_progress: true # Enable visual progress tracking
allowed_bots: '*'
prompt: |
Review Basic Memory PR #${{ inputs.pr_number }} as an advisory manual review.
Use `gh pr view ${{ inputs.pr_number }}` and related `gh pr`/`gh api`
commands to inspect the pull request. Do not merge the PR and do not
treat this advisory review as the required merge gate. BM Bossbot owns
the required `BM Bossbot Approval` status.
Review the PR against our team checklist:
## Code Quality & Standards
- [ ] Follows Basic Memory's coding conventions in CLAUDE.md
- [ ] Python 3.12+ type annotations and async patterns
- [ ] SQLAlchemy 2.0 best practices
- [ ] FastAPI and Typer conventions followed
- [ ] 100-character line length limit maintained
- [ ] No commented-out code blocks
## Testing & Documentation
- [ ] Unit tests for new functions/methods
- [ ] Integration tests for new MCP tools
- [ ] Test coverage for edge cases
- [ ] **100% test coverage maintained** (use `# pragma: no cover` only for truly hard-to-test code)
- [ ] Documentation updated (README, docstrings)
- [ ] CLAUDE.md updated if conventions change
## Basic Memory Architecture
- [ ] MCP tools follow atomic, composable design
- [ ] Database changes include Alembic migrations
- [ ] Preserves local-first architecture principles
- [ ] Knowledge graph operations maintain consistency
- [ ] Markdown file handling preserves integrity
- [ ] AI-human collaboration patterns followed
## Security & Performance
- [ ] No hardcoded secrets or credentials
- [ ] Input validation for MCP tools
- [ ] Proper error handling and logging
- [ ] Performance considerations addressed
- [ ] No sensitive data in logs or commits
## Compatability
- [ ] File path comparisons must be windows compatible
- [ ] Avoid using emojis and unicode characters in console and log output
Read the CLAUDE.md file for detailed project context. For each checklist item, verify if it's satisfied and comment on any that need attention. Use inline comments for specific code issues and post a summary with checklist results.
# Allow broader tool access for thorough code review
claude_args: '--allowed-tools "Bash(gh pr:*),Bash(gh issue:*),Bash(gh api:*),Bash(git log:*),Bash(git show:*),Read,Grep,Glob"'
-74
View File
@@ -1,74 +0,0 @@
name: Claude Issue Triage
on:
issues:
types: [opened]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
triage:
runs-on: ubuntu-latest
permissions:
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Run Claude Issue Triage
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true # Show triage progress
prompt: |
Analyze this new Basic Memory issue and perform triage:
**Issue Analysis:**
1. **Type Classification:**
- Bug report (code defect)
- Feature request (new functionality)
- Enhancement (improvement to existing feature)
- Documentation (docs improvement)
- Question/Support (user help)
- MCP tool issue (specific to MCP functionality)
2. **Priority Assessment:**
- Critical: Security issues, data loss, complete breakage
- High: Major functionality broken, affects many users
- Medium: Minor bugs, usability issues
- Low: Nice-to-have improvements, cosmetic issues
3. **Component Classification:**
- CLI commands
- MCP tools
- Database/sync
- Cloud functionality
- Documentation
- Testing
4. **Complexity Estimate:**
- Simple: Quick fix, documentation update
- Medium: Requires some investigation/testing
- Complex: Major feature work, architectural changes
**Actions to Take:**
1. Add appropriate labels using: `gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2"`
2. Check for duplicates using: `gh search issues`
3. If duplicate found, comment mentioning the original issue
4. For feature requests, ask clarifying questions if needed
5. For bugs, request reproduction steps if missing
**Available Labels:**
- Type: bug, enhancement, feature, documentation, question, mcp-tool
- Priority: critical, high, medium, low
- Component: cli, mcp, database, cloud, docs, testing
- Complexity: simple, medium, complex
- Status: needs-reproduction, needs-clarification, duplicate
Read the issue carefully and provide helpful triage with appropriate labels.
claude_args: '--allowed-tools "Bash(gh issue:*),Bash(gh search:*),Read"'
-70
View File
@@ -1,70 +0,0 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
pull_request_target:
types: [opened, synchronize]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
claude:
if: |
(
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@claude'))
) && (
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR' ||
github.event.sender.author_association == 'OWNER' ||
github.event.sender.author_association == 'MEMBER' ||
github.event.sender.author_association == 'COLLABORATOR' ||
github.event.pull_request.author_association == 'OWNER' ||
github.event.pull_request.author_association == 'MEMBER' ||
github.event.pull_request.author_association == 'COLLABORATOR'
)
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
# For pull_request_target, checkout the PR head to review the actual changes
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
fetch-depth: 1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true # Enable visual progress tracking
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'
# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.claude.com/en/docs/claude-code/sdk#command-line for available options
# claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)'
-110
View File
@@ -1,110 +0,0 @@
name: Consolidated Packages
on:
pull_request:
paths:
- ".claude-plugin/**"
- "plugins/**"
- "skills/**"
- "integrations/**"
- "scripts/update_versions.py"
- "scripts/validate_*.py"
- "justfile"
- ".github/workflows/consolidated-packages.yml"
push:
branches:
- main
paths:
- ".claude-plugin/**"
- "plugins/**"
- "skills/**"
- "integrations/**"
- "scripts/update_versions.py"
- "scripts/validate_*.py"
- "justfile"
- ".github/workflows/consolidated-packages.yml"
permissions:
contents: read
jobs:
claude-code:
name: Claude Code marketplace
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v4
- name: Validate manifests, agent, and bundled skills
run: just --justfile plugins/claude-code/justfile --working-directory plugins/claude-code ci-check
- name: Verify shared version dry run
run: just release-dry-run v0.99.0
skills:
name: Shared skills
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v4
- name: Validate SKILL.md source
run: just --justfile skills/justfile --working-directory skills check
hermes:
name: Hermes unit tests
permissions:
contents: read
runs-on: ubuntu-latest
defaults:
run:
working-directory: integrations/hermes
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Set up Python
run: uv python install 3.12
- name: Validate manifest and run unit tests
run: just check
openclaw:
name: OpenClaw package
permissions:
contents: read
runs-on: ubuntu-latest
defaults:
run:
working-directory: integrations/openclaw
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.8"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Release readiness
run: just release-check
-56
View File
@@ -1,56 +0,0 @@
name: Dev Release
on:
push:
branches: [main]
workflow_dispatch: # Allow manual triggering
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
dev-release:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install uv
run: |
pip install uv
- name: Install dependencies and build
run: |
uv venv
uv sync
uv build
- name: Check if this is a dev version
id: check_version
run: |
VERSION=$(uv run python -c "import basic_memory; print(basic_memory.__version__)")
echo "version=$VERSION" >> $GITHUB_OUTPUT
if [[ "$VERSION" == *"dev"* ]]; then
echo "is_dev=true" >> $GITHUB_OUTPUT
echo "Dev version detected: $VERSION"
else
echo "is_dev=false" >> $GITHUB_OUTPUT
echo "Release version detected: $VERSION, skipping dev release"
fi
- name: Publish dev version to PyPI
if: steps.check_version.outputs.is_dev == 'true'
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_TOKEN }}
skip-existing: true # Don't fail if version already exists
-59
View File
@@ -1,59 +0,0 @@
name: Docker Image CI
on:
push:
tags:
- 'v*' # Trigger on version tags like v1.0.0, v0.13.0, etc.
workflow_dispatch: # Allow manual triggering for testing
env:
REGISTRY: ghcr.io
IMAGE_NAME: basicmachines-co/basic-memory
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
docker:
runs-on: depot-ubuntu-24.04
permissions:
contents: read
id-token: write
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Depot
uses: depot/setup-action@v1
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: depot/build-push-action@v1
with:
project: ${{ vars.DEPOT_BASIC_MEMORY_PROJECT_ID || vars.DEPOT_PROJECT_ID }}
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+2 -9
View File
@@ -7,14 +7,11 @@ on:
- edited
- synchronize
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v6
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -38,11 +35,7 @@ jobs:
mcp
sync
ui
ci
deps
installer
plugins
skills
integrations
# Allow breaking changes (needs "!" after type/scope)
requireScopeForBreakingChange: true
requireScopeForBreakingChange: true
+66 -135
View File
@@ -1,165 +1,96 @@
name: Release
on:
push:
tags:
- 'v*' # Trigger on version tags like v1.0.0, v0.13.0, etc.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
workflow_dispatch:
inputs:
version_type:
description: 'Type of version bump (major, minor, patch)'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
jobs:
release:
runs-on: ubuntu-latest
concurrency: release
permissions:
id-token: write
contents: write
outputs:
released: ${{ steps.release.outputs.released }}
tag: ${{ steps.release.outputs.tag }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
- name: Python Semantic Release
id: release
uses: python-semantic-release/python-semantic-release@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: steps.release.outputs.released == 'true'
with:
password: ${{ secrets.PYPI_TOKEN }}
- name: Publish to GitHub Release Assets
uses: python-semantic-release/publish-action@v9.8.9
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}
build-macos:
needs: release
if: needs.release.outputs.released == 'true'
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.tag }}
- name: Set up Python "3.12"
uses: actions/setup-python@v4
with:
python-version: "3.12"
cache: 'pip'
- name: Install librsvg
run: brew install librsvg
- name: Install uv
run: |
pip install uv
- name: Install dependencies and build
- name: Create virtual env
run: |
uv venv
uv sync
uv build
- name: Verify build succeeded
run: |
# Verify that build artifacts exist
ls -la dist/
echo "Build completed successfully"
- name: Create GitHub Release
uses: softprops/action-gh-release@v3
with:
files: |
dist/*.whl
dist/*.tar.gz
generate_release_notes: true
tag_name: ${{ github.ref_name }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_TOKEN }}
openclaw:
name: Publish OpenClaw npm Package
needs: release
# npm publishes only for stable product tags. Pre-release Python tags use
# versions like 0.21.3b1, which are not valid npm pre-release semver.
if: ${{ !contains(github.ref_name, 'dev') && !contains(github.ref_name, 'b') && !contains(github.ref_name, 'rc') }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
defaults:
run:
working-directory: integrations/openclaw
steps:
- uses: actions/checkout@v6
- uses: extractions/setup-just@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.8"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Release readiness
run: just release-check
- name: Publish to npm
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
homebrew:
name: Update Homebrew Formula
needs: release
runs-on: ubuntu-latest
# Only run for stable releases (not dev, beta, or rc versions)
if: ${{ !contains(github.ref_name, 'dev') && !contains(github.ref_name, 'b') && !contains(github.ref_name, 'rc') }}
permissions:
contents: read
steps:
# Inline bump replaces mislav/bump-homebrew-formula-action@v4.x.
# The action does a HEAD request to api.github.com /repos/.../tarball/<ref>
# with the bearer token and expects a 302 redirect. GitHub now returns
# 303 on that endpoint when authenticated, which the action treats as a
# fatal error. Re-implementing the bump as plain git+sed keeps the same
# contract (update url + sha256, commit, push) with no third-party action.
- name: Update Homebrew formula
env:
HOMEBREW_TOKEN: ${{ secrets.HOMEBREW_TOKEN }}
REF: ${{ github.ref_name }}
REPO: ${{ github.repository }}
RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -euo pipefail
uv sync
VERSION="${REF#v}"
ARCHIVE_URL="https://github.com/${REPO}/archive/refs/tags/${REF}.tar.gz"
- name: Build macOS installer
run: |
make installer-mac
xattr -dr com.apple.quarantine "installer/build/Basic Memory Installer.app"
echo "::group::Compute tarball sha256"
SHA256="$(curl --fail --silent --location "$ARCHIVE_URL" | sha256sum | awk '{print $1}')"
test -n "$SHA256"
echo "sha256: $SHA256"
echo "::endgroup::"
- name: Zip macOS installer
run: |
cd installer/build
zip -ry "Basic-Memory-Installer-${{ needs.release.outputs.tag }}.zip" "Basic Memory Installer.app"
echo "::group::Clone tap"
git clone \
--depth 1 \
"https://x-access-token:${HOMEBREW_TOKEN}@github.com/basicmachines-co/homebrew-basic-memory.git" \
tap
cd tap
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
echo "::endgroup::"
echo "::group::Patch Formula/basic-memory.rb"
# Pipe-delimited sed because the URL contains slashes. The Formula
# only has one `url` and one `sha256` directive, so a first-match
# replacement is unambiguous. POSIX character classes ([[:space:]])
# keep this portable across BSD and GNU sed.
sed -i -E \
-e "s|^([[:space:]]*url[[:space:]]+)\"[^\"]+\"|\1\"${ARCHIVE_URL}\"|" \
-e "s|^([[:space:]]*sha256[[:space:]]+)\"[^\"]+\"|\1\"${SHA256}\"|" \
Formula/basic-memory.rb
git --no-pager diff Formula/basic-memory.rb
echo "::endgroup::"
if git diff --quiet Formula/basic-memory.rb; then
echo "Formula already at ${REF}; nothing to do."
exit 0
fi
echo "::group::Commit & push"
git add Formula/basic-memory.rb
git commit -m "basic-memory ${VERSION}
Created by ${RUN_URL}"
git push origin HEAD:main
echo "::endgroup::"
- name: Upload macOS installer
uses: softprops/action-gh-release@v1
with:
files: installer/build/Basic-Memory-Installer-${{ needs.release.outputs.tag }}.zip
tag_name: ${{ needs.release.outputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
+17 -377
View File
@@ -1,407 +1,47 @@
name: Tests
concurrency:
group: bm-ci-${{ github.workflow }}-${{ github.repository }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
on:
# Trigger: PR branch pushes already publish commit statuses that show up on the PR.
# Why: running the full matrix on both push and pull_request doubles CI time for the
# exact same branch head commit.
# Outcome: each branch push runs the test suite once, including PR updates.
push:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
# Branch builds (PRs arrive as push events — this workflow has no
# pull_request trigger) select only impacted tests from the cached testmon
# baseline (branch cache falling back to main's full-run recording). Pushes
# to main run the full suite with --testmon-noselect to refresh the baseline.
BASIC_MEMORY_TESTMON_FLAGS: ${{ github.ref_name == 'main' && '--testmon-noselect' || '--testmon --testmon-forceselect' }}
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
changes:
# Docs/workflow-only changes skip the entire test matrix while the workflow
# still concludes successfully, so the BM Bossbot gate (workflow_run on
# Tests success) keeps firing and the PR stays mergeable.
name: Detect code changes
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@v6
- id: filter
uses: dorny/paths-filter@v3
with:
# Tests only runs on push events; for branch pushes compare against
# main (merge-base), for main pushes dorny diffs the push range.
base: main
filters: |
code:
- 'src/**'
- 'tests/**'
- 'test-int/**'
- 'alembic/**'
- 'pyproject.toml'
- 'uv.lock'
- 'justfile'
- '.github/workflows/test.yml'
static-checks:
needs: changes
if: needs.changes.outputs.code == 'true'
name: Static Checks (Python 3.12)
timeout-minutes: 20
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ "3.12" ]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python 3.12
uses: actions/setup-python@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: "3.12"
cache: "pip"
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install uv
run: |
pip install uv
- uses: extractions/setup-just@v4
- name: Create virtual env
run: |
uv venv
- name: Install dependencies
run: |
uv pip install -e ".[dev]"
uv pip install -e .[dev]
- name: Run type checks
run: |
just typecheck
- name: Run linting
run: |
just lint
test-sqlite-unit:
needs: changes
if: needs.changes.outputs.code == 'true'
name: Test SQLite Unit (${{ matrix.os }}, Python ${{ matrix.python-version }})
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: "3.12"
- os: ubuntu-latest
python-version: "3.13"
# Python 3.14 unit tests are the longest full-suite slice; keep this
# one on GitHub-hosted runners after Depot terminated it mid-suite.
- os: ubuntu-latest
python-version: "3.14"
- os: windows-latest
python-version: "3.12"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install uv
run: |
pip install uv
- uses: extractions/setup-just@v4
- name: Cache pytest-testmon results
uses: actions/cache@v4
with:
path: |
.testmondata
.testmondata-shm
.testmondata-wal
key: ${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}-${{ github.ref_name }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}-${{ github.ref_name }}-
${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}-main-
${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}-
- name: Create virtual env
run: |
uv venv
- name: Install dependencies
run: |
uv pip install -e ".[dev]"
uv run make type-check
- name: Run tests
run: |
just test-unit-sqlite
test-sqlite-integration:
needs: changes
if: needs.changes.outputs.code == 'true'
name: Test SQLite Integration (${{ matrix.os }}, Python ${{ matrix.python-version }})
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: "3.12"
- os: ubuntu-latest
python-version: "3.13"
- os: ubuntu-latest
python-version: "3.14"
- os: windows-latest
python-version: "3.12"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install uv
run: |
pip install uv
- uses: extractions/setup-just@v4
- name: Cache pytest-testmon results
uses: actions/cache@v4
with:
path: |
.testmondata
.testmondata-shm
.testmondata-wal
key: ${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}-${{ github.ref_name }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}-${{ github.ref_name }}-
${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}-main-
${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}-
- name: Create virtual env
run: |
uv venv
- name: Install dependencies
run: |
uv pip install -e ".[dev]"
- name: Run tests
run: |
just test-int-sqlite
test-postgres-unit:
needs: changes
if: needs.changes.outputs.code == 'true'
name: Test Postgres Unit (Python ${{ matrix.python-version }}, shard ${{ matrix.group }}/3)
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
# Shard the largest suite across parallel jobs: each shard is a full job
# with its own Postgres service running 1/3 of the collection.
# Postgres runs on the latest Python only — the SQLite matrix carries
# Python-version coverage; Postgres carries backend coverage.
group: [1, 2, 3]
python-version: ["3.14"]
runs-on: ubuntu-latest
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: basic_memory_user
POSTGRES_PASSWORD: dev_password
POSTGRES_DB: basic_memory_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U basic_memory_user -d basic_memory_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
BASIC_MEMORY_TEST_POSTGRES_URL: postgresql://basic_memory_user:dev_password@127.0.0.1:5432/basic_memory_test
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install uv
run: |
pip install uv
- uses: extractions/setup-just@v4
- name: Cache pytest-testmon results
uses: actions/cache@v4
with:
path: |
.testmondata
.testmondata-shm
.testmondata-wal
key: ${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-g${{ matrix.group }}-${{ github.ref_name }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-g${{ matrix.group }}-${{ github.ref_name }}-
${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-g${{ matrix.group }}-main-
${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-main-
${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-
- name: Create virtual env
run: |
uv venv
- name: Install dependencies
run: |
uv pip install -e ".[dev]"
- name: Run tests
run: |
BASIC_MEMORY_PYTEST_SPLIT_FLAGS="--splits 3 --group ${{ matrix.group }}" just test-unit-postgres
test-postgres-integration:
needs: changes
if: needs.changes.outputs.code == 'true'
name: Test Postgres Integration (Python ${{ matrix.python-version }})
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
# Latest Python only: SQLite carries version coverage, Postgres carries
# backend coverage.
python-version: ["3.14"]
runs-on: ubuntu-latest
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: basic_memory_user
POSTGRES_PASSWORD: dev_password
POSTGRES_DB: basic_memory_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U basic_memory_user -d basic_memory_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
BASIC_MEMORY_TEST_POSTGRES_URL: postgresql://basic_memory_user:dev_password@127.0.0.1:5432/basic_memory_test
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install uv
run: |
pip install uv
- uses: extractions/setup-just@v4
- name: Cache pytest-testmon results
uses: actions/cache@v4
with:
path: |
.testmondata
.testmondata-shm
.testmondata-wal
key: ${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}-${{ github.ref_name }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}-${{ github.ref_name }}-
${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}-main-
${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}-
- name: Create virtual env
run: |
uv venv
- name: Install dependencies
run: |
uv pip install -e ".[dev]"
- name: Run tests
run: |
just test-int-postgres
test-semantic:
needs: changes
if: needs.changes.outputs.code == 'true'
name: Test Semantic (Python 3.12)
timeout-minutes: 45
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: "pip"
- name: Install uv
run: |
pip install uv
- uses: extractions/setup-just@v4
- name: Cache pytest-testmon results
uses: actions/cache@v4
with:
path: |
.testmondata
.testmondata-shm
.testmondata-wal
key: ${{ runner.os }}-testmon-semantic-py3.12-${{ github.ref_name }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-testmon-semantic-py3.12-${{ github.ref_name }}-
${{ runner.os }}-testmon-semantic-py3.12-main-
${{ runner.os }}-testmon-semantic-py3.12-
- name: Create virtual env
run: |
uv venv
- name: Install dependencies
run: |
uv pip install -e ".[dev]"
- name: Run tests
run: |
just test-semantic
uv pip install pytest pytest-cov
uv run make test
+2 -20
View File
@@ -1,7 +1,6 @@
*.py[cod]
__pycache__/
.pytest_cache/
.testmondata*
.coverage
htmlcov/
@@ -43,25 +42,8 @@ ENV/
# macOS
.DS_Store
.coverage.*
/.coverage.*
# obsidian docs:
/docs/.obsidian/
/examples/.obsidian/
/examples/.basic-memory/
/docs/assets
# claude action
claude-output
**/.claude/settings.local.json
.mcp.json
!/plugins/codex/.mcp.json
.mcpregistry_*
/.testmondata
.benchmarks/
# Consolidated package build artifacts
/integrations/openclaw/node_modules/
/integrations/openclaw/dist/
/integrations/openclaw/skills/
/integrations/openclaw/*.tgz
/examples/.obsidian/
+1 -1
View File
@@ -1 +1 @@
3.14
3.12
-540
View File
@@ -1,540 +0,0 @@
# AGENTS.md - Basic Memory Project Guide
## Project Overview
Basic Memory is a local-first knowledge management system built on the Model Context Protocol (MCP). It enables
bidirectional communication between LLMs (like Claude) and markdown files, creating a personal knowledge graph that can
be traversed using links between documents.
## CODEBASE DEVELOPMENT
### Project information
See the [README.md](README.md) file for a project overview.
### Build and Test Commands
- Install: `just install` or `pip install -e ".[dev]"`
- Run all tests (SQLite + Postgres): `just test`
- Run all tests against SQLite: `just test-sqlite`
- Run all tests against Postgres: `just test-postgres` (uses testcontainers)
- Run unit tests (SQLite): `just test-unit-sqlite`
- Run unit tests (Postgres): `just test-unit-postgres`
- Run integration tests (SQLite): `just test-int-sqlite`
- Run integration tests (Postgres): `just test-int-postgres`
- Run impacted tests: `just testmon` (pytest-testmon; only tests affected by changed code)
- Run MCP smoke test: `just test-smoke`
- Fast local loop: `just fast-check` (default iteration flow)
- Local consistency check: `just doctor`
- Run all consolidated agent package checks: `just package-check`
- Run Claude Code plugin checks: `just package-check-claude-code`
- Run shared skills checks: `just package-check-skills`
- Run Hermes plugin checks: `just package-check-hermes`
- Run OpenClaw plugin checks: `just package-check-openclaw`
- Run host-native agent harness checks: `just agent-harness-check`
- Generate HTML coverage: `just coverage`
- Single test: `pytest tests/path/to/test_file.py::test_function_name`
- Run benchmarks: `pytest test-int/test_sync_performance_benchmark.py -v -m "benchmark and not slow"`
- Lint: `just lint` or `ruff check . --fix`
- Type check: `just typecheck` or `uv run ty check src tests test-int`
- Type check (pyright): `just typecheck-pyright` or `uv run pyright`
- Format: `just format` or `uv run ruff format .`
- Run all code checks: `just check` (runs lint, format, typecheck, test)
- Create db migration: `just migration "Your migration message"`
- Run development MCP Inspector: `just run-inspector`
**Note:** Project requires Python 3.12+ (uses type parameter syntax and `type` aliases introduced in 3.12)
**Postgres Testing:** Uses [testcontainers](https://testcontainers-python.readthedocs.io/) which automatically spins up a Postgres instance in Docker. No manual database setup required - just have Docker running.
**Doctor Note:** `just doctor` runs with a temporary HOME/config so it won't touch your local Basic Memory settings. It leaves temp dirs in `/tmp` (safe to ignore or remove).
**Testmon Note:** When no files have changed, `just testmon` may collect 0 tests. That's expected and means no impacted tests were detected.
### Code/Test/Verify Loop (fast path)
1) **Code:** make changes.
2) **Test:** `just fast-check` (lint/format/typecheck + pytest-testmon impacted tests for changed code).
3) **Verify:** `just doctor` (end-to-end file ↔ DB loop in a temp project).
4) **Package verify:** `just package-check` when changes touch `plugins/`, `skills/`, `integrations/`, package metadata, or release wiring.
5) **Full gate (when needed):** `just test` or `just check` for SQLite + Postgres.
Run `just test-smoke` when you specifically need the MCP smoke flow.
If testmon is “cold,” the first run may be long. Subsequent runs get much faster.
### Consolidated Agent Package Checks
The monorepo ships several host-native packages alongside the Python core. Use the root justfile as the canonical entry point:
- `just package-check` — validates every copied package and generated bundle path.
- `just package-check-claude-code` — validates the root and plugin-local Claude marketplace manifests, the SessionStart/PreCompact hooks, the bundled output style, and the seed schemas, then runs `claude plugin validate . --strict`.
- `just package-check-skills` — validates every top-level `skills/memory-*/SKILL.md` frontmatter block.
- `just package-check-hermes` — validates `integrations/hermes/plugin.yaml`, the Hermes provider entrypoint, bundled skill, and runs the hermetic unit suite.
- `just package-check-openclaw` — runs the OpenClaw package install, copies top-level skills into the generated bundle, typechecks, lints, builds `dist/`, runs Bun tests, and performs `npm pack --dry-run`.
- `just agent-harness-check` — checks the host-specific harnesses without the shared markdown-only skills target.
Package-local justfiles live in `plugins/claude-code/`, `skills/`, `integrations/hermes/`, and `integrations/openclaw/`. Prefer the root targets for PR verification so command names stay stable as package internals evolve.
### PR CI Gate
Before opening or updating a PR, run the checks that mirror the common required CI failures:
- Run `just typecheck` in addition to targeted `ruff` and `pytest` commands when tests were added or changed.
- Sign commits with `git commit -s` so DCO passes. If a PR branch already has unsigned commits, rewrite the branch with signed-off commits before asking for review.
- Use a semantic PR title accepted by `.github/workflows/pr-title.yml`: `type(scope): summary`.
- Use one of the allowed scopes: `core`, `cli`, `api`, `mcp`, `sync`, `ui`, `ci`, `deps`, `installer`, `plugins`, `skills`, `integrations`.
### Test Structure
- `tests/` - Unit tests for individual components (mocked, fast)
- `test-int/` - Integration tests for real-world scenarios (no mocks, realistic)
- Both directories are covered by unified coverage reporting
- Benchmark tests in `test-int/` are marked with `@pytest.mark.benchmark`
- Slow tests are marked with `@pytest.mark.slow`
- Smoke tests are marked with `@pytest.mark.smoke`
### Code Style Guidelines
- Line length: 100 characters max
- Python 3.12+ with full type annotations (uses type parameters and type aliases)
- Format with ruff (consistent styling)
- Import order: standard lib, third-party, local imports
- Naming: snake_case for functions/variables, PascalCase for classes
- Prefer async patterns with SQLAlchemy 2.0
- Use Pydantic v2 for data validation and schemas
- CLI uses Typer for command structure
- API uses FastAPI for endpoints
- Follow the repository pattern for data access
- Tools communicate to api routers via the httpx ASGI client (in process)
### Programming Style
See [docs/ENGINEERING_STYLE.md](docs/ENGINEERING_STYLE.md) for the fuller house style. The
short version for agents:
- Prefer type-safe, explicit designs over object-heavy indirection. Use Python 3.12 `type`
aliases, full annotations, and narrow `Protocol`s when a caller only needs a capability.
- Use dataclasses for internal value objects and operation results; use Pydantic v2 at API,
CLI, MCP, and persistence boundaries where validation and serialization matter.
- Keep async boundaries obvious. Resource-owning code should use context managers, propagate
cancellation, and avoid hidden background work unless the lifecycle is explicit.
- Fail fast. Do not add silent fallback logic, broad exception swallowing, speculative
`getattr`, or casts that hide an unclear model shape.
- Keep control flow simple and local. Push branching decisions up, keep leaf helpers focused,
and name values after the domain concept they carry.
- Use evidence-first testing. Add or update meaningful regression tests for bugs and risky
behavior, prefer real code paths over mocks, and run the narrowest command that proves the
change before widening verification.
- Comments should explain why a branch, invariant, or constraint exists. Avoid comments that
merely narrate obvious code.
### Code Change Guidelines
- **Full file read before edits**: Before editing any file, read it in full first to ensure complete context; partial reads lead to corrupted edits
- **Minimize diffs**: Prefer the smallest change that satisfies the request. Avoid unrelated refactors or style rewrites unless necessary for correctness
- **House style is canonical**: Follow the Programming Style section above for type-safe,
fail-fast code; do not hide unclear models with speculative attributes, broad exception
handling, casts, or unapproved fallback logic
- **No guessing**: Do not say "The issue is..." before you actually know what the issue is. Investigate first.
### Literate Programming Style
Code should tell a story. Comments must explain the "why" and narrative flow, not just the "what".
**Section Headers:**
For files with multiple phases of logic, add section headers so the control flow reads like chapters:
```python
# --- Authentication ---
# ... auth logic ...
# --- Data Validation ---
# ... validation logic ...
# --- Business Logic ---
# ... core logic ...
```
**Decision Point Comments:**
For conditionals that materially change behavior (gates, fallbacks, retries, feature flags), add comments with:
- **Trigger**: what condition causes this branch
- **Why**: the rationale (cost, correctness, UX, determinism)
- **Outcome**: what changes downstream
```python
# Trigger: project has no active sync watcher
# Why: avoid duplicate file system watchers consuming resources
# Outcome: starts new watcher, registers in active_watchers dict
if project_id not in active_watchers:
start_watcher(project_id)
```
**Constraint Comments:**
If code exists because of a constraint (async requirements, rate limits, schema compatibility), explain the constraint near the code:
```python
# SQLite requires WAL mode for concurrent read/write access
connection.execute("PRAGMA journal_mode=WAL")
```
**What NOT to Comment:**
Avoid comments that restate obvious code:
```python
# Bad - restates code
counter += 1 # increment counter
# Good - explains why
counter += 1 # track retries for backoff calculation
```
### Codebase Architecture
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed architecture documentation.
**Directory Structure:**
- `/alembic` - Alembic db migrations
- `/api` - FastAPI REST endpoints + `container.py` composition root
- `/cli` - Typer CLI + `container.py` composition root
- `/deps` - Feature-scoped FastAPI dependencies (config, db, projects, repositories, services, importers)
- `/importers` - Import functionality for Claude, ChatGPT, and other sources
- `/markdown` - Markdown parsing and processing
- `/mcp` - MCP server + `container.py` composition root + `clients/` typed API clients
- `/models` - SQLAlchemy ORM models
- `/repository` - Data access layer
- `/schemas` - Pydantic models for validation
- `/services` - Business logic layer
- `/sync` - File synchronization services + `coordinator.py` for lifecycle management
- `/plugins/claude-code` - Claude Code plugin marketplace package, hooks, skills, and agent harness
- `/skills` - Canonical framework-agnostic Basic Memory `SKILL.md` source
- `/integrations/hermes` - Hermes memory-provider plugin
- `/integrations/openclaw` - OpenClaw npm/TypeScript plugin
**Composition Roots:**
Each entrypoint (API, MCP, CLI) has a composition root that:
- Reads `ConfigManager` (the only place that reads global config)
- Resolves runtime mode via `RuntimeMode` enum (TEST > CLOUD > LOCAL)
- Provides dependencies to downstream code explicitly
**Typed API Clients (MCP):**
MCP tools use typed clients in `mcp/clients/` to communicate with the API:
- `KnowledgeClient` - Entity CRUD operations
- `SearchClient` - Search operations
- `MemoryClient` - Context building
- `DirectoryClient` - Directory listing
- `ResourceClient` - Resource reading
- `ProjectClient` - Project management
Flow: MCP Tool → Typed Client → HTTP API → Router → Service → Repository
### Development Notes
- MCP tools are defined in src/basic_memory/mcp/tools/
- MCP prompts are defined in src/basic_memory/mcp/prompts/
- MCP tools should be atomic, composable operations
- Use `textwrap.dedent()` for multi-line string formatting in prompts and tools
- MCP Prompts are used to invoke tools and format content with instructions for an LLM
- Schema changes require Alembic migrations
- SQLite is used for indexing and full text search, files are source of truth
- Testing uses pytest with asyncio support (strict mode)
- Unit tests (`tests/`) use mocks when necessary; integration tests (`test-int/`) use real implementations
- By default, tests run against SQLite (fast, no Docker needed)
- Set `BASIC_MEMORY_TEST_POSTGRES=1` to run against Postgres (uses testcontainers - Docker required)
- Each test runs in a standalone environment with isolated database and tmp_path directory
- CI runs SQLite and Postgres tests in parallel for faster feedback
- Performance benchmarks are in `test-int/test_sync_performance_benchmark.py`
- Use pytest markers: `@pytest.mark.benchmark` for benchmarks, `@pytest.mark.slow` for slow tests
- **Coverage must stay at 100%**: Write tests for new code. Only use `# pragma: no cover` when tests would require excessive mocking (e.g., TYPE_CHECKING blocks, error handlers that need failure injection, runtime-mode-dependent code paths)
### Async Client Pattern (Important!)
**MCP tools use `get_project_client()` for per-project routing:**
```python
from basic_memory.mcp.project_context import get_project_client
@mcp.tool()
async def my_tool(project: str | None = None, context: Context | None = None):
async with get_project_client(project, context) as (client, active_project):
# client is routed based on project's mode (local ASGI or cloud HTTP)
response = await call_get(client, "/path")
return response
```
**CLI commands and non-project-scoped code use `get_client()` directly:**
```python
from basic_memory.mcp.async_client import get_client
async def my_cli_command():
async with get_client() as client:
response = await call_get(client, "/path")
return response
# Per-project routing (when project name is known):
async with get_client(project_name="research") as client:
...
```
**Do NOT use:**
-`from basic_memory.mcp.async_client import client` (deprecated module-level client)
- ❌ Manual auth header management
-`inject_auth_header()` (deleted)
- ❌ Separate `get_client()` + `get_active_project()` in MCP tools (use `get_project_client()` instead)
**Key principles:**
- Auth happens at client creation, not per-request
- Proper resource management via context managers
- Per-project routing: each project can be LOCAL or CLOUD independently
- Cloud projects use API key (`cloud_api_key` in config) as Bearer token
- Routing priority: factory injection > force-local > per-project cloud > global cloud > local ASGI
- Factory pattern enables dependency injection for cloud consolidation
**For cloud app integration:**
```python
from basic_memory.mcp import async_client
# Set custom factory before importing tools
async_client.set_client_factory(your_custom_factory)
```
See SPEC-16 for full context manager refactor details.
### Release Process
Releases are driven by `just release` / `just beta` — never by a bare `git tag`. The recipes bump version metadata, run pre-flight checks, commit, tag, and push. GitHub Actions then publishes to PyPI and updates the Homebrew formula.
**Stable release:**
```
just release v0.21.3
```
The recipe runs `just lint` + `just typecheck`, then updates every release manifest through `scripts/update_versions.py`: `src/basic_memory/__init__.py`, `server.json`, the root Claude marketplace, the Claude Code plugin manifest and local marketplace, the Hermes `plugin.yaml`, and the OpenClaw `package.json`. It commits as `chore: update version to X.Y.Z for vX.Y.Z release`, creates the `vX.Y.Z` tag, and pushes both the commit and the tag to `origin/main`. After the tag lands, the `Release` workflow builds the Python package, publishes to PyPI, creates the GitHub release with auto-generated notes, publishes the OpenClaw npm package, and updates the Homebrew formula. The recipe finishes by printing the post-release tasks the workflow doesn't cover.
**Beta release:** `just beta v0.21.3b1` — same flow with a beta-suffixed tag. PyPI consumers install with `pip install basic-memory --pre`.
**Release dry run:** `just release-dry-run v0.21.4` previews the consolidated version update without writing files.
**Development builds:** every commit to `main` publishes a `0.21.3.dev26+468a22f`-style version to PyPI automatically via `.github/workflows/dev-release.yml`. No human action.
**Do not tag releases by hand.** A bare `git tag vX.Y.Z` skips the in-code version bump. Package metadata is still correct (uv-dynamic-versioning derives it from the git tag) but `basic-memory --version` reports the previous release, which is what happened with v0.21.2 → v0.21.3.
**Post-release tasks** the recipe surfaces but doesn't run:
- `docs.basicmemory.com` — add notes to `src/pages/latest-releases.mdx`
- `basicmachines.co` — bump version in `src/components/sections/hero.tsx`
- MCP Registry — `mcp-publisher publish` from the repo root
See `.claude/commands/release/release.md` (and `beta.md`, `release-check.md`, `changelog.md` alongside it) for the full release + post-release runbook, including the slash commands.
## BASIC MEMORY PRODUCT USAGE
### Knowledge Structure
- Entity: Any concept, document, or idea represented as a markdown file
- Observation: A categorized fact about an entity (`- [category] content`)
- Relation: A directional link between entities (`- relation_type [[Target]]`)
- Frontmatter: YAML metadata at the top of markdown files
- Knowledge representation follows precise markdown format:
- Observations with [category] prefixes
- Relations with WikiLinks [[Entity]]
- Frontmatter with metadata
### Basic Memory Commands
**Local Commands:**
- Check sync status: `basic-memory status`
- Doctor check (file <-> DB loop): `basic-memory doctor`
- Import from Claude: `basic-memory import claude conversations`
- Import from ChatGPT: `basic-memory import chatgpt`
- Import from Memory JSON: `basic-memory import memory-json`
- Tool access: `basic-memory tool` (provides CLI access to MCP tools)
- Continue: `basic-memory tool continue-conversation --topic="search"`
**Project Management:**
- List projects: `basic-memory project list`
- Add project: `basic-memory project add "name" ~/path`
- Project info: `basic-memory project info`
- Set cloud mode: `basic-memory project set-cloud "name"`
- Set local mode: `basic-memory project set-local "name"`
- One-way sync (local -> cloud): `basic-memory project sync`
- Bidirectional sync: `basic-memory project bisync`
- Integrity check: `basic-memory project check`
**Cloud Commands (requires subscription):**
- Authenticate (global): `basic-memory cloud login`
- Logout (global): `basic-memory cloud logout`
- Check cloud status: `basic-memory cloud status`
- Setup cloud sync: `basic-memory cloud setup`
- Save API key: `basic-memory cloud set-key bmc_...`
- Create API key: `basic-memory cloud create-key "name"`
- Manage snapshots: `basic-memory cloud snapshot [create|list|delete|show|browse]`
- Restore from snapshot: `basic-memory cloud restore <path> --snapshot <id>`
**Cloud Sync Commands (Personal and Team workspaces):**
- Fetch cloud changes (cloud -> local): `basic-memory cloud pull --name "name"` (Team-safe; additive, never deletes local)
- Upload local changes (local -> cloud): `basic-memory cloud push --name "name"` (Team-safe; additive, never deletes cloud)
- Resolve conflicts on push/pull: `--on-conflict [fail|keep-local|keep-cloud|keep-both]` (default `fail` lists conflicts and aborts, git-style)
- One-way mirror (local -> cloud): `basic-memory cloud sync --name "name"` (Personal workspaces only; deletes cloud files missing locally)
- Two-way mirror (local <-> cloud): `basic-memory cloud bisync --name "name"` (Personal workspaces only)
### MCP Capabilities
- Basic Memory exposes these MCP tools to LLMs:
**Content Management:**
- `write_note(title, content, directory, tags)` - Create/update markdown notes with semantic observations and relations
- `read_note(identifier, page, page_size)` - Read notes by title, permalink, or memory:// URL with knowledge graph awareness
- `read_content(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
- `view_note(identifier, page, page_size)` - View notes as formatted artifacts for better readability
- `edit_note(identifier, operation, content)` - Edit notes incrementally (append, prepend, find/replace, replace_section)
- `move_note(identifier, destination_path, is_directory)` - Move notes or directories to new locations, updating database and maintaining links
- `delete_note(identifier, is_directory)` - Delete notes or directories from the knowledge base
**Knowledge Graph Navigation:**
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation continuity
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "1d", "1 week")
- `list_directory(dir_name, depth, file_name_glob)` - Browse directory contents with filtering and depth control
**Search & Discovery:**
- `search_notes(query, page, page_size, search_type, types, entity_types, after_date)` - Full-text search across all content with advanced filtering options
**Project Management:**
- `list_memory_projects()` - List all available projects with their status
- `create_memory_project(project_name, project_path, set_default)` - Create new Basic Memory projects
- `delete_project(project_name)` - Delete a project from configuration
**Visualization:**
- `canvas(nodes, edges, title, directory)` - Generate Obsidian canvas files for knowledge graph visualization
**ChatGPT-Compatible Tools:**
- `search(query)` - Search across knowledge base (OpenAI actions compatible)
- `fetch(id)` - Fetch full content of a search result document
- MCP Prompts for better AI interaction:
- `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
- `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
- `search(query, after_date)` - Search with detailed, formatted results for better context understanding
- `recent_activity(timeframe)` - View recently changed items with formatted output
### Cloud Features (v0.15.0+)
Basic Memory now supports cloud synchronization and storage (requires active subscription):
**Authentication:**
- JWT-based authentication with subscription validation
- Secure session management with token refresh
- Support for multiple cloud projects
**Bidirectional Sync:**
- rclone bisync integration for two-way synchronization
- Conflict resolution and integrity verification
- Real-time sync with change detection
- Mount/unmount cloud storage for direct file access
**Cloud Project Management:**
- Create and manage projects in the cloud
- Toggle between local and cloud modes
- Per-project sync configuration
- Subscription-based access control
**Security & Performance:**
- Removed .env file loading for improved security
- .gitignore integration (respects gitignored files)
- WAL mode for SQLite performance
- Background relation resolution (non-blocking startup)
- API performance optimizations (SPEC-11)
**Per-Project Cloud Routing:**
Individual projects can be routed through the cloud while others stay local, using an API key:
```bash
# Save API key and set project to cloud mode
basic-memory cloud set-key bmc_abc123...
basic-memory project set-cloud research # route through cloud
basic-memory project set-local research # revert to local
```
MCP tools use `get_project_client()` which automatically routes based on the project's mode. Cloud projects use the `cloud_api_key` from config as Bearer token.
**CLI Routing Flags (Global Cloud Mode):**
When global cloud mode is enabled, CLI commands route to the cloud API by default. Use `--local` and `--cloud` flags to override:
```bash
# Force local routing (ignore cloud mode)
basic-memory status --local
basic-memory project list --local
# Force cloud routing (when cloud mode is disabled)
basic-memory status --cloud
basic-memory project info my-project --cloud
```
Key behaviors:
- The local MCP server (`basic-memory mcp`) automatically uses local routing
- This allows simultaneous use of local Claude Desktop and cloud-based clients
- Some commands (like `project default`, `project sync-config`, `project move`) require `--local` in cloud mode since they modify local configuration
- Environment variable `BASIC_MEMORY_FORCE_LOCAL=true` forces local routing globally
- Per-project cloud routing via API key works independently of global cloud mode
## AI-Human Collaborative Development
Basic Memory emerged from and enables a new kind of development process that combines human and AI capabilities. Instead
of using AI just for code generation, we've developed a true collaborative workflow:
1. AI (LLM) writes initial implementation based on specifications and context
2. Human reviews, runs tests, and commits code with any necessary adjustments
3. Knowledge persists across conversations using Basic Memory's knowledge graph
4. Development continues seamlessly across different AI sessions with consistent context
5. Results improve through iterative collaboration and shared understanding
This approach has allowed us to tackle more complex challenges and build a more robust system than either humans or AI
could achieve independently.
**Problem-Solving Guidance:**
- If a solution isn't working after reasonable effort, suggest alternative approaches
- Don't persist with a problematic library or pattern when better alternatives exist
- Example: When py-pglite caused cascading test failures, switching to testcontainers-postgres was the right call
## GitHub Integration
Basic Memory has taken AI-Human collaboration to the next level by integrating Claude directly into the development workflow through GitHub:
### GitHub MCP Tools
Using the GitHub Model Context Protocol server, Claude can now:
- **Repository Management**:
- View repository files and structure
- Read file contents
- Create new branches
- Create and update files
- **Issue Management**:
- Create new issues
- Comment on existing issues
- Close and update issues
- Search across issues
- **Pull Request Workflow**:
- Create pull requests
- Review code changes
- Add comments to PRs
This integration enables Claude to participate as a full team member in the development process, not just as a code generation tool. Claude's GitHub account ([bm-claudeai](https://github.com/bm-claudeai)) is a member of the Basic Machines organization with direct contributor access to the codebase.
### Collaborative Development Process
With GitHub integration, the development workflow includes:
1. **Direct code review** - Claude can analyze PRs and provide detailed feedback
2. **Contribution tracking** - All of Claude's contributions are properly attributed in the Git history
3. **Branch management** - Claude can create feature branches for implementations
4. **Documentation maintenance** - Claude can keep documentation updated as the code evolves
5. **Code Commits**: ALWAYS sign off commits with `git commit -s`
6. **Pull Request Titles**: PR titles must follow the semantic format enforced by `.github/workflows/pr-title.yml`: `type(scope): summary`
- Allowed types: `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`
- Allowed scopes: `core`, `cli`, `api`, `mcp`, `sync`, `ui`, `ci`, `deps`, `installer`, `plugins`, `skills`, `integrations`
- Example: `fix(cli): propagate cloud workspace routing`
This level of integration represents a new paradigm in AI-human collaboration, where the AI assistant becomes a full-fledged team member rather than just a tool for generating code snippets.
+3 -2488
View File
File diff suppressed because it is too large Load Diff
+26 -63
View File
@@ -1,71 +1,34 @@
# Contributor License Agreement
Developer Certificate of Origin
Version 1.1
https://developercertificate.org/
## Copyright Assignment and License Grant
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
By signing this Contributor License Agreement ("Agreement"), you accept and agree to the following terms and conditions
for your present and future Contributions submitted
to Basic Machines LLC. Except for the license granted herein to Basic Machines LLC and recipients of software
distributed by Basic Machines LLC, you reserve all right,
title, and interest in and to your Contributions.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
### 1. Definitions
Developer's Certificate of Origin 1.1
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this
Agreement with Basic Machines LLC.
By making a contribution to this project, I certify that:
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work,
that is intentionally submitted by You to Basic
Machines LLC for inclusion in, or documentation of, any of the products owned or managed by Basic Machines LLC (the "
Work").
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
### 2. Grant of Copyright License
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
software distributed by Basic Machines LLC a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Work, and to permit persons to whom the Work is furnished to do so.
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
### 3. Assignment of Copyright
You hereby assign to Basic Machines LLC all right, title, and interest worldwide in all Copyright covering your
Contributions. Basic Machines LLC may license the
Contributions under any license terms, including copyleft, permissive, commercial, or proprietary licenses.
### 4. Grant of Patent License
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
software distributed by Basic Machines LLC a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to
make, have made, use, offer to sell, sell, import, and
otherwise transfer the Work.
### 5. Developer Certificate of Origin
By making a Contribution to this project, You certify that:
(a) The Contribution was created in whole or in part by You and You have the right to submit it under this Agreement; or
(b) The Contribution is based upon previous work that, to the best of Your knowledge, is covered under an appropriate
open source license and You have the right under that
license to submit that work with modifications, whether created in whole or in part by You, under this Agreement; or
(c) The Contribution was provided directly to You by some other person who certified (a), (b) or (c) and You have not
modified it.
(d) You understand and agree that this project and the Contribution are public and that a record of the Contribution (
including all personal information You submit with
it, including Your sign-off) is maintained indefinitely and may be redistributed consistent with this project or the
open source license(s) involved.
### 6. Representations
You represent that you are legally entitled to grant the above license and assignment. If your employer(s) has rights to
intellectual property that you create that
includes your Contributions, you represent that you have received permission to make Contributions on behalf of that
employer, or that your employer has waived such rights
for your Contributions to Basic Machines LLC.
---
This Agreement is effective as of the date you first submit a Contribution to Basic Machines LLC.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
-1
View File
@@ -1 +0,0 @@
AGENTS.md
+133
View File
@@ -0,0 +1,133 @@
# CLAUDE.md - Basic Memory Project Guide
## Project Overview
Basic Memory is a local-first knowledge management system built on the Model Context Protocol (MCP). It enables
bidirectional communication between LLMs (like Claude) and markdown files, creating a personal knowledge graph that can
be traversed using links between documents.
## CODEBASE DEVELOPMENT
### Project information
See the [README.md](README.md) file for a project overview.
### Build and Test Commands
- Install: `make install` or `pip install -e ".[dev]"`
- Run tests: `uv run pytest -p pytest_mock -v` or `make test`
- Single test: `pytest tests/path/to/test_file.py::test_function_name`
- Lint: `make lint` or `ruff check . --fix`
- Type check: `make type-check` or `uv run pyright`
- Format: `make format` or `uv run ruff format .`
- Run all code checks: `make check` (runs lint, format, type-check, test)
- Create db migration: `make migration m="Your migration message"`
- Run development MCP Inspector: `make run-inspector`
### Code Style Guidelines
- Line length: 100 characters max
- Python 3.12+ with full type annotations
- Format with ruff (consistent styling)
- Import order: standard lib, third-party, local imports
- Naming: snake_case for functions/variables, PascalCase for classes
- Prefer async patterns with SQLAlchemy 2.0
- Use Pydantic v2 for data validation and schemas
- CLI uses Typer for command structure
- API uses FastAPI for endpoints
- Follow the repository pattern for data access
- Tools communicate to api routers via the httpx ASGI client (in process)
### Codebase Architecture
- `/alembic` - Alembic db migrations
- `/api` - FastAPI implementation of REST endpoints
- `/cli` - Typer command-line interface
- `/markdown` - Markdown parsing and processing
- `/mcp` - Model Context Protocol server implementation
- `/models` - SQLAlchemy ORM models
- `/repository` - Data access layer
- `/schemas` - Pydantic models for validation
- `/services` - Business logic layer
- `/sync` - File synchronization services
### Development Notes
- MCP tools are defined in src/basic_memory/mcp/tools/
- MCP prompts are defined in src/basic_memory/mcp/prompts/
- MCP tools should be atomic, composable operations
- Use `textwrap.dedent()` for multi-line string formatting in prompts and tools
- MCP Prompts are used to invoke tools and format content with instructions for an LLM
- Schema changes require Alembic migrations
- SQLite is used for indexing and full text search, files are source of truth
- Testing uses pytest with asyncio support (strict mode)
- Test database uses in-memory SQLite
- Avoid creating mocks in tests in most circumstances.
- Each test runs in a standalone enviroment with in memory SQLite and tmp_file directory
## BASIC MEMORY PRODUCT USAGE
### Knowledge Structure
- Entity: Any concept, document, or idea represented as a markdown file
- Observation: A categorized fact about an entity (`- [category] content`)
- Relation: A directional link between entities (`- relation_type [[Target]]`)
- Frontmatter: YAML metadata at the top of markdown files
- Knowledge representation follows precise markdown format:
- Observations with [category] prefixes
- Relations with WikiLinks [[Entity]]
- Frontmatter with metadata
### Basic Memory Commands
- Sync knowledge: `basic-memory sync` or `basic-memory sync --watch`
- Import from Claude: `basic-memory import claude conversations`
- Import from ChatGPT: `basic-memory import chatgpt`
- Import from Memory JSON: `basic-memory import memory-json`
- Check sync status: `basic-memory status`
- Tool access: `basic-memory tools` (provides CLI access to MCP tools)
- Guide: `basic-memory tools basic-memory-guide`
- Continue: `basic-memory tools continue-conversation --topic="search"`
### MCP Capabilities
- Basic Memory exposes these MCP tools to LLMs:
**Content Management:**
- `write_note(title, content, folder, tags)` - Create/update markdown notes with semantic observations and relations
- `read_note(identifier, page, page_size)` - Read notes by title, permalink, or memory:// URL with knowledge graph
awareness
- `read_file(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
**Knowledge Graph Navigation:**
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation
continuity
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "
1d", "1 week")
**Search & Discovery:**
- `search(query, page, page_size)` - Full-text search across all content with filtering options
**Visualization:**
- `canvas(nodes, edges, title, folder)` - Generate Obsidian canvas files for knowledge graph visualization
- MCP Prompts for better AI interaction:
- `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
- `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
- `search(query, after_date)` - Search with detailed, formatted results for better context understanding
- `recent_activity(timeframe)` - View recently changed items with formatted output
- `json_canvas_spec()` - Full JSON Canvas specification for Obsidian visualization
## AI-Human Collaborative Development
Basic Memory emerged from and enables a new kind of development process that combines human and AI capabilities. Instead
of using AI just for code generation, we've developed a true collaborative workflow:
1. AI (LLM) writes initial implementation based on specifications and context
2. Human reviews, runs tests, and commits code with any necessary adjustments
3. Knowledge persists across conversations using Basic Memory's knowledge graph
4. Development continues seamlessly across different AI sessions with consistent context
5. Results improve through iterative collaboration and shared understanding
This approach has allowed us to tackle more complex challenges and build a more robust system than either humans or AI
could achieve independently.
+49 -133
View File
@@ -1,7 +1,6 @@
# Contributing to Basic Memory
Thank you for considering contributing to Basic Memory! This document outlines the process for contributing to the
project and how to get started as a developer.
Thank you for considering contributing to Basic Memory! This document outlines the process for contributing to the project and how to get started as a developer.
## Getting Started
@@ -15,8 +14,8 @@ project and how to get started as a developer.
2. **Install Dependencies**:
```bash
# Using just (recommended)
just install
# Using make (recommended)
make install
# Or using uv
uv install -e ".[dev]"
@@ -25,27 +24,13 @@ project and how to get started as a developer.
pip install -e ".[dev]"
```
> **Note**: Basic Memory uses [just](https://just.systems) as a modern command runner. Install with `brew install just` or `cargo install just`.
3. **Activate the Virtual Environment**
3. **Run the Tests**:
```bash
source .venv/bin/activate
```
4. **Run the Tests**:
```bash
# Run all tests with unified coverage (unit + integration)
just test
# Run unit tests only (fast, no coverage)
just test-unit
# Run integration tests only (fast, no coverage)
just test-int
# Generate HTML coverage report
just coverage
# Run all tests
make test
# or
uv run pytest -p pytest_mock -v
# Run a specific test
pytest tests/path/to/test_file.py::test_function_name
```
@@ -63,16 +48,16 @@ project and how to get started as a developer.
4. **Check Code Quality**:
```bash
# Run all checks at once
just check
make check
# Or run individual checks
just lint # Run linting
just format # Format code
just type-check # Type checking
make lint # Run linting
make format # Format code
make type-check # Type checking
```
5. **Test Your Changes**: Ensure all tests pass locally and maintain 100% test coverage.
```bash
just test
make test
```
6. **Submit a PR**: Submit a pull request with a detailed description of your changes.
@@ -80,68 +65,65 @@ project and how to get started as a developer.
This project is designed for collaborative development between humans and LLMs (Large Language Models):
1. **CLAUDE.md**: The repository includes a `CLAUDE.md` file that serves as a project guide for both humans and LLMs.
This file contains:
- Key project information and architectural overview
- Development commands and workflows
- Code style guidelines
- Documentation standards
1. **CLAUDE.md**: The repository includes a `CLAUDE.md` file that serves as a project guide for both humans and LLMs. This file contains:
- Key project information and architectural overview
- Development commands and workflows
- Code style guidelines
- Documentation standards
2. **AI-Human Collaborative Workflow**:
- We encourage using LLMs like Claude for code generation, reviews, and documentation
- When possible, save context in markdown files that can be referenced later
- This enables seamless knowledge transfer between different development sessions
- Claude can help with implementation details while you focus on architecture and design
- We encourage using LLMs like Claude for code generation, reviews, and documentation
- When possible, save context in markdown files that can be referenced later
- This enables seamless knowledge transfer between different development sessions
- Claude can help with implementation details while you focus on architecture and design
3. **Adding to CLAUDE.md**:
- If you discover useful project information or common commands, consider adding them to CLAUDE.md
- This helps all contributors (human and AI) maintain consistent knowledge of the project
- If you discover useful project information or common commands, consider adding them to CLAUDE.md
- This helps all contributors (human and AI) maintain consistent knowledge of the project
## Pull Request Process
1. **Create a Pull Request**: Open a PR against the `main` branch with a clear title and description.
2. **Sign the Developer Certificate of Origin (DCO)**: All contributions require signing our DCO, which certifies that
you have the right to submit your contributions. This will be automatically checked by our CLA assistant when you
create a PR.
2. **Sign the Developer Certificate of Origin (DCO)**: All contributions require signing our DCO, which certifies that you have the right to submit your contributions. This will be automatically checked by our CLA assistant when you create a PR.
3. **PR Description**: Include:
- What the PR changes
- Why the change is needed
- How you tested the changes
- Any related issues (use "Fixes #123" to automatically close issues)
- What the PR changes
- Why the change is needed
- How you tested the changes
- Any related issues (use "Fixes #123" to automatically close issues)
4. **Code Review**: Wait for code review and address any feedback.
5. **CI Checks**: Ensure all CI checks pass.
6. **Merge**: Once approved, a maintainer will merge your PR.
## Developer Certificate of Origin
By contributing to this project, you agree to the [Developer Certificate of Origin (DCO)](CLA.md). This means you
certify that:
By contributing to this project, you agree to the [Developer Certificate of Origin (DCO)](CLA.md). This means you certify that:
- You have the right to submit your contributions
- You're not knowingly submitting code with patent or copyright issues
- Your contributions are provided under the project's license (AGPL-3.0)
This is a lightweight alternative to a Contributor License Agreement and helps ensure that all contributions can be
properly incorporated into the project and potentially used in commercial applications.
This is a lightweight alternative to a Contributor License Agreement and helps ensure that all contributions can be properly incorporated into the project and potentially used in commercial applications.
### Signing Your Commits
Sign your commit:
You can sign your commits in one of two ways:
**Using the `-s` or `--signoff` flag**:
1. **Using the `-s` or `--signoff` flag**:
```bash
git commit -s -m "Your commit message"
```
This adds a `Signed-off-by` line to your commit message, certifying that you adhere to the DCO.
```bash
git commit -s -m "Your commit message"
```
2. **Configuring Git to automatically sign off**:
```bash
git config --global alias.cs 'commit -s'
```
Then use `git cs -m "Your commit message"` to commit with sign-off.
This adds a `Signed-off-by` line to your commit message, certifying that you adhere to the DCO.
The sign-off certifies that you have the right to submit your contribution under the project's license and verifies your
agreement to the DCO.
The sign-off certifies that you have the right to submit your contribution under the project's license and verifies your agreement to the DCO.
## Code Style Guidelines
- **Python Version**: Python 3.12+ with full type annotations (3.12+ required for type parameter syntax)
- **Python Version**: Python 3.12+ with full type annotations
- **Line Length**: 100 characters maximum
- **Formatting**: Use ruff for consistent styling
- **Import Order**: Standard lib, third-party, local imports
@@ -151,78 +133,12 @@ agreement to the DCO.
## Testing Guidelines
### Test Structure
Basic Memory uses two test directories with unified coverage reporting:
- **`tests/`**: Unit tests that test individual components in isolation
- Fast execution with extensive mocking
- Test individual functions, classes, and modules
- Run with: `just test-unit` (no coverage, fast)
- **`test-int/`**: Integration tests that test real-world scenarios
- Test full workflows with real database and file operations
- Include performance benchmarks
- More realistic but slower than unit tests
- Run with: `just test-int` (no coverage, fast)
### Running Tests
```bash
# Run all tests with unified coverage report
just test
# Run only unit tests (fast iteration)
just test-unit
# Run only integration tests
just test-int
# Generate HTML coverage report
just coverage
# Run specific test
pytest tests/path/to/test_file.py::test_function_name
# Run tests excluding benchmarks
pytest -m "not benchmark"
# Run only benchmark tests
pytest -m benchmark test-int/test_sync_performance_benchmark.py
```
### Performance Benchmarks
The `test-int/test_sync_performance_benchmark.py` file contains performance benchmarks that measure sync and indexing speed:
- `test_benchmark_sync_100_files` - Small repository performance
- `test_benchmark_sync_500_files` - Medium repository performance
- `test_benchmark_sync_1000_files` - Large repository performance (marked slow)
- `test_benchmark_resync_no_changes` - Re-sync performance baseline
Run benchmarks with:
```bash
# Run all benchmarks (excluding slow ones)
pytest test-int/test_sync_performance_benchmark.py -v -m "benchmark and not slow"
# Run all benchmarks including slow ones
pytest test-int/test_sync_performance_benchmark.py -v -m benchmark
# Run specific benchmark
pytest test-int/test_sync_performance_benchmark.py::test_benchmark_sync_100_files -v
```
See `test-int/BENCHMARKS.md` for detailed benchmark documentation.
### Testing Best Practices
- **Coverage Target**: We aim for high test coverage for all code
- **Coverage Target**: We aim for 100% test coverage for all code
- **Test Framework**: Use pytest for unit and integration tests
- **Mocking**: Avoid mocking in integration tests; use sparingly in unit tests
- **Mocking**: Use pytest-mock for mocking dependencies
- **Edge Cases**: Test both normal operation and edge cases
- **Database Testing**: Use in-memory SQLite for testing database operations
- **Fixtures**: Use async pytest fixtures for setup and teardown
- **Markers**: Use `@pytest.mark.benchmark` for benchmarks, `@pytest.mark.slow` for slow tests
- **Fixtures**: Use pytest fixtures for setup and teardown
## Creating Issues
@@ -240,4 +156,4 @@ All contributors must follow the [Code of Conduct](CODE_OF_CONDUCT.md).
## Thank You!
Your contributions help make Basic Memory better. We appreciate your time and effort!
Your contributions help make Basic Memory better for everyone. We appreciate your time and effort!
+10 -46
View File
@@ -1,52 +1,16 @@
FROM python:3.12-slim-bookworm
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM python:3.12-slim
# Build arguments for user ID and group ID (defaults to 1000)
ARG UID=1000
ARG GID=1000
# Copy uv from official image
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Set environment variables
# UV_PYTHON_INSTALL_DIR ensures Python is installed to a persistent location
# that survives in the final image (not in /root/.local which gets lost)
# UV_PYTHON_PREFERENCE=only-managed tells uv to use its managed Python version
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
UV_PYTHON_INSTALL_DIR=/python \
UV_PYTHON_PREFERENCE=only-managed
# Create a group and user with the provided UID/GID
# Check if the GID already exists, if not create appgroup
RUN (getent group ${GID} || groupadd --gid ${GID} appgroup) && \
useradd --uid ${UID} --gid ${GID} --create-home --shell /bin/bash appuser
# Copy the project into the image
ADD . /app
# Install Python 3.13 explicitly and sync the project
WORKDIR /app
RUN uv python install 3.13
RUN uv sync --locked --python 3.13
# Create necessary directories and set ownership
RUN mkdir -p /app/data/basic-memory /app/.basic-memory && \
chown -R appuser:${GID} /app
# Copy the project files
COPY . .
# Set default data directory and add venv to PATH
ENV BASIC_MEMORY_HOME=/app/data/basic-memory \
BASIC_MEMORY_PROJECT_ROOT=/app/data \
PATH="/app/.venv/bin:$PATH"
# Install pip and build dependencies
RUN pip install --upgrade pip \
&& pip install . --no-cache-dir --ignore-installed
# Switch to the non-root user
USER appuser
# Expose port if necessary (e.g., uv might use a port, but MCP over stdio so not needed here)
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD basic-memory --version || exit 1
# Use the basic-memory entrypoint to run the MCP server with default SSE transport
CMD ["basic-memory", "mcp", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]
# Use the basic-memory entrypoint to run the MCP server
CMD ["basic-memory", "mcp"]
+67
View File
@@ -0,0 +1,67 @@
.PHONY: install test test-module lint clean format type-check installer-mac installer-win check
install:
pip install -e ".[dev]"
test:
uv run pytest -p pytest_mock -v
# Run tests for a specific module
# Usage: make test-module m=path/to/module.py [cov=module_path]
test-module:
@if [ -z "$(m)" ]; then \
echo "Usage: make test-module m=path/to/module.py [cov=module_path]"; \
exit 1; \
fi; \
if [ -z "$(cov)" ]; then \
uv run pytest $(m) -v; \
else \
uv run pytest $(m) -v --cov=$(cov); \
fi
lint:
ruff check . --fix
type-check:
uv run pyright
clean:
find . -type f -name '*.pyc' -delete
find . -type d -name '__pycache__' -exec rm -r {} +
rm -rf installer/build/
rm -rf installer/dist/
rm -f rw.*.dmg
rm -rf dist
rm -rf installer/build
rm -rf installer/dist
rm -f .coverage.*
format:
uv run ruff format .
# run inspector tool
run-inspector:
uv run mcp dev src/basic_memory/mcp/main.py
# Build app installer
installer-mac:
cd installer && chmod +x make_icons.sh && ./make_icons.sh
cd installer && uv run python setup.py bdist_mac
installer-win:
cd installer && uv run python setup.py bdist_win32
update-deps:
uv lock --upgrade
check: lint format type-check test
# Target for generating Alembic migrations with a message from command line
migration:
@if [ -z "$(m)" ]; then \
echo "Usage: make migration m=\"Your migration message\""; \
exit 1; \
fi; \
cd src/basic_memory/alembic && alembic revision --autogenerate -m "$(m)"
-494
View File
@@ -1,494 +0,0 @@
# Note Format Reference
Every document in Basic Memory is a plain Markdown file. Files are the source of truth — changes to files automatically update the knowledge graph in the database. You maintain complete ownership, files work with git, and knowledge persists independently of any AI conversation.
## Document Structure
A note has three parts: YAML frontmatter, content (observations), and relations.
```markdown
---
title: Coffee Brewing Methods
type: note
tags: [coffee, brewing]
permalink: coffee-brewing-methods
---
# Coffee Brewing Methods
## Observations
- [method] Pour over provides more flavor clarity than French press
- [technique] Water temperature at 205°F extracts optimal compounds #brewing
- [preference] Ethiopian beans work well with lighter roasts (personal experience)
## Relations
- relates_to [[Coffee Bean Origins]]
- requires [[Proper Grinding Technique]]
- contrasts_with [[Tea Brewing Methods]]
```
The `## Observations` and `## Relations` headings are conventional but not required — the parser detects observations and relations by their syntax patterns anywhere in the document.
## Frontmatter
YAML metadata between `---` fences at the top of the file.
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `title` | No | filename stem | Used for linking and references. Auto-set from filename if missing. |
| `type` | No | `note` | Entity type. Used for schema resolution and filtering. |
| `tags` | No | `[]` | List or comma-separated string. Used for organization and search. |
| `permalink` | No | generated from title | Stable identifier. Persists even if the file moves. |
| `schema` | No | none | Schema attachment — dict (inline), string (reference), or omitted (implicit). |
Custom fields are allowed. Any key not in the standard set is stored as `entity_metadata` and indexed for search and filtering.
```yaml
---
title: Paul Graham
type: Person
tags: [startups, essays, lisp]
permalink: paul-graham
status: active
source: wikipedia
---
```
Here `status` and `source` are custom fields stored in `entity_metadata`.
### Frontmatter Value Handling
YAML automatically converts some values to native types. Basic Memory normalizes them:
- Date strings (`2025-10-24`) → kept as ISO format strings
- Numbers (`1.0`) → converted to strings
- Booleans (`true`) → converted to strings (`"True"`)
- Lists and dicts → preserved, items normalized recursively
This prevents errors when downstream code expects string values.
## Observations
An observation is a categorized fact about the entity. Written as a Markdown list item.
**Syntax:**
```
- [category] content text #tag1 #tag2 (context)
```
| Part | Required | Description |
|------|----------|-------------|
| `[category]` | Yes | Classification in square brackets. Any text except `[]()` chars. |
| content | Yes | The fact or statement. |
| `#tags` | No | Inline tags. Space-separated, each starting with `#`. |
| `(context)` | No | Parenthesized text at end of line. Supporting details or source. |
### Examples
```markdown
- [tech] Uses SQLite for storage #database
- [design] Follows local-first architecture #architecture
- [decision] Selected bcrypt for passwords #security (based on OWASP audit)
- [name] Paul Graham
- [expertise] Startups
- [expertise] Lisp
- [expertise] Essay writing
```
Array-like fields use repeated categories — multiple `[expertise]` observations above.
### What Is Not an Observation
The parser excludes these list item patterns:
| Pattern | Example | Reason |
|---------|---------|--------|
| Checkboxes | `- [ ] Todo item`, `- [x] Done`, `- [-] Cancelled` | Task list syntax |
| Markdown links | `- [text](url)` | URL link syntax |
| Bare wiki links | `- [[Target]]` | Treated as a relation instead |
A list item with `#tags` but no `[category]` is still parsed — the tags are extracted and the category defaults to `Note`.
## Relations
Relations connect documents to form the knowledge graph. There are two kinds.
### Explicit Relations
Written as list items with a relation type and a `[[wiki link]]` target.
**Syntax:**
```
- relation_type [[Target Entity]] (context)
```
| Part | Required | Description |
|------|----------|-------------|
| `relation_type` | No | Text before `[[`. Defaults to `relates_to` if omitted. |
| `[[Target]]` | Yes | Wiki link to the target entity. Matched by title or permalink. |
| `(context)` | No | Parenthesized text after `]]`. Supporting details. |
### Examples
```markdown
- implements [[Search Design]]
- depends_on [[Database Schema]]
- works_at [[Y Combinator]] (co-founder)
- [[Some Entity]]
```
The last example — a bare `[[wiki link]]` in a list item — gets relation type `relates_to`.
Common relation types:
- `implements`, `depends_on`, `relates_to`, `inspired_by`
- `extends`, `part_of`, `contains`, `pairs_with`
- `works_at`, `authored`, `collaborated_with`
Any text works as a relation type. These are conventions, not a fixed set.
### Inline References
Wiki links appearing in regular prose (not as list items) create implicit `links_to` relations.
```markdown
This builds on [[Core Design]] and uses [[Utility Functions]].
```
This creates two relations: `links_to [[Core Design]]` and `links_to [[Utility Functions]]`.
### Forward References
Relations can link to entities that don't exist yet. Basic Memory resolves them when the target is created.
## Permalinks and memory:// URLs
Every document has a unique **permalink** — a stable identifier derived from its title. You can set one explicitly in frontmatter, or let the system generate it.
```yaml
permalink: auth-approaches-2024
```
Permalinks form the basis of `memory://` URLs:
```
memory://auth-approaches-2024 # By permalink
memory://Authentication Approaches # By title (auto-resolves)
memory://project/auth-approaches # By path
```
Pattern matching is supported:
```
memory://auth* # Starts with "auth"
memory://*/approaches # Ends with "approaches"
memory://project/*/requirements # Nested wildcard
```
## Schemas
Schemas declare the expected structure of a note — which observation categories and relation types a well-formed note should have. They use Picoschema, a compact notation from Google's Dotprompt that fits naturally in YAML frontmatter.
### Picoschema Syntax
```yaml
schema:
name: string, full name # required field with description
email?: string, contact email # ? = optional
role?: string, job title
works_at?: Organization, employer # capitalized type = entity reference
tags?(array): string, categories # array of type
status?(enum): [active, inactive] # enum with allowed values
metadata?(object): # nested object
updated_at?: string
source?: string
```
| Notation | Meaning | Example |
|----------|---------|---------|
| `field: type` | Required field | `name: string` |
| `field?: type` | Optional field | `role?: string` |
| `field(array): type` | Array of values | `expertise(array): string` |
| `field?(enum): [vals]` | Enum with allowed values | `status?(enum): [active, inactive]` |
| `field?(object):` | Nested object with sub-fields | `metadata?(object):` |
| `, description` | Description after comma | `name: string, full name` |
| `EntityName` | Capitalized type = entity reference | `works_at?: Organization` |
**Scalar types:** `string`, `integer`, `number`, `boolean`, `any`
Any type not in that set whose first letter is uppercase is treated as an entity reference (a relation target).
### Schema-to-Note Mapping
Schemas validate against existing observation/relation syntax. Note authors don't learn new syntax.
| Schema Declaration | Maps To | Example in Note |
|--------------------|---------|-----------------|
| `field: string` | Observation `[field] value` | `- [name] Paul Graham` |
| `field?(array): string` | Multiple `[field]` observations | `- [expertise] Lisp` (repeated) |
| `field?: EntityType` | Relation `field [[Target]]` | `- works_at [[Y Combinator]]` |
| `field?(array): EntityType` | Multiple `field` relations | `- authored [[Book]]` (repeated) |
| `tags` | Frontmatter `tags` array | `tags: [startups, essays]` |
| `field?(enum): [vals]` | Observation `[field] value` where value is in the set | `- [status] active` |
Observations and relations not covered by the schema are valid — schemas describe a subset, not a straitjacket.
### Schema Attachment
Three ways to attach a schema to a note, resolved in priority order:
**1. Inline schema**`schema` is a dict in frontmatter:
```yaml
---
title: Team Standup 2024-01-15
type: meeting
schema:
attendees(array): string, who was there
decisions(array): string, what was decided
action_items(array): string, follow-ups
blockers?(array): string, anything stuck
---
```
Good for one-off structured notes or prototyping a schema before extracting it.
**2. Explicit reference**`schema` is a string naming a schema note:
```yaml
---
title: Basic Memory
schema: SoftwareProject
---
```
or by permalink:
```yaml
---
title: LLM Memory Patterns
schema: schema/research-project
---
```
Use when the note's `type` differs from the schema it should validate against, or when multiple schema variants exist.
**3. Implicit by type** — no `schema` field, resolved by matching `type`:
```yaml
---
title: Paul Graham
type: Person
---
```
The system looks up a schema note where `entity: Person`. If found, it applies. If not, no validation occurs.
**4. No schema** — perfectly fine. Most notes don't need one.
### Schema Notes
A schema is itself a Basic Memory note with `type: schema`. It lives anywhere (though `schema/` is the conventional directory).
```yaml
# schema/Person.md
---
title: Person
type: schema
entity: Person
version: 1
schema:
name: string, full name
role?: string, job title or position
works_at?: Organization, employer
expertise?(array): string, areas of knowledge
email?: string, contact email
settings:
validation: warn
---
# Person
A human individual in the knowledge graph.
```
| Field | Required | Description |
|-------|----------|-------------|
| `type` | Yes | Must be `schema` |
| `entity` | Yes | The entity type this schema describes (e.g., `Person`) |
| `version` | No | Schema version number (default: `1`) |
| `schema` | Yes | Picoschema dict defining the fields |
| `settings.validation` | No | Validation mode (default: `warn`) |
Schema notes are regular notes — they show up in search, can have observations and relations, and participate in the knowledge graph.
### Validation Modes
| Mode | Behavior |
|------|----------|
| `warn` | Warnings in output, doesn't block (default) |
| `strict` | Errors that block sync, for CI/CD enforcement |
| `off` | No validation |
### Validation Output
```
$ bm schema validate people/ada-lovelace.md
⚠ Person schema validation:
- Missing required field: name (expected [name] observation)
- Missing optional field: role
- Missing optional field: works_at (no relation found)
Unmatched observations: [fact] ×2, [born] ×1
Unmatched relations: collaborated_with
```
"Unmatched" items are informational — observations and relations the schema doesn't cover.
### Schema Inference
Generate schemas from existing notes by analyzing observation and relation frequency:
```
$ bm schema infer Person
Analyzing 30 notes with type: Person...
Observations found:
[name] 30/30 100% → name: string
[role] 27/30 90% → role?: string
[expertise] 18/30 60% → expertise?(array): string
[email] 8/30 27% → email?: string
Relations found:
works_at 22/30 73% → works_at?: Organization
Suggested schema:
name: string, full name
role?: string, job title
expertise?(array): string, areas of knowledge
email?: string, contact email
works_at?: Organization, employer
Save to schema/Person.md? [y/n]
```
Frequency thresholds:
- **100% present** → required field
- **25%+ present** → optional field
- **Below 25%** → excluded from suggestion
### Schema Drift Detection
Track how usage patterns shift over time:
```
$ bm schema diff Person
Schema drift detected:
+ expertise: now in 81% of notes (was 12%)
- department: dropped to 3% of notes
~ works_at: cardinality changed (one → many)
Update schema? [y/n/review]
```
## Complete Examples
### Simple Note (No Schema)
```markdown
---
title: Project Ideas
type: note
tags: [ideas, brainstorm]
---
# Project Ideas
## Observations
- [idea] Build a CLI tool for markdown linting #tooling
- [idea] Create a recipe knowledge base #cooking
- [priority] Focus on developer tools first (Q1 goal)
## Relations
- inspired_by [[Developer Workflow Research]]
- part_of [[Q1 Planning]]
```
### Schema-Validated Note
Schema at `schema/Person.md`:
```yaml
---
title: Person
type: schema
entity: Person
version: 1
schema:
name: string, full name
role?: string, job title or position
works_at?: Organization, employer
expertise?(array): string, areas of knowledge
email?: string, contact email
settings:
validation: warn
---
# Person
A human individual in the knowledge graph.
```
Note at `people/paul-graham.md`:
```markdown
---
title: Paul Graham
type: Person
tags: [startups, essays, lisp]
---
# Paul Graham
## Observations
- [name] Paul Graham
- [role] Essayist and investor
- [expertise] Startups
- [expertise] Lisp
- [expertise] Essay writing
- [fact] Created Viaweb, the first web app
## Relations
- works_at [[Y Combinator]]
- authored [[Hackers and Painters]]
```
The `[fact]` observation and `authored` relation are not in the schema — they're valid, just unmatched. The schema only checks that `[name]` exists (required) and looks for optional fields like `[role]`, `[expertise]`, and `works_at`.
### Inline Schema Note
```markdown
---
title: Team Standup 2024-01-15
type: meeting
schema:
attendees(array): string, who was there
decisions(array): string, what was decided
action_items(array): string, follow-ups
blockers?(array): string, anything stuck
---
# Team Standup 2024-01-15
## Observations
- [attendees] Paul
- [attendees] Sarah
- [decisions] Ship v2 by Friday
- [action_items] Paul to review PR #42
- [blockers] Waiting on API credentials
```
+580 -514
View File
File diff suppressed because it is too large Load Diff
-78
View File
@@ -1,78 +0,0 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 0.x.x | :white_check_mark: |
## Reporting a Vulnerability
If you find a vulnerability, please contact hello@basicmachines.co.
Please do not open a public GitHub issue for security vulnerabilities. We aim
to respond within 72 hours and will coordinate a fix and disclosure timeline
with you.
## Threat Model
Basic Memory is a local-first MCP server that reads and writes markdown files
inside configured project directories. It runs on your machine with your user
permissions, so local configuration deserves the same care as any other
developer tool that can access your files.
### What Basic Memory Controls
- Filesystem-touching tools validate paths against the configured project root
with `validate_project_path()`, resolved paths, and `Path.is_relative_to()`.
Path traversal attempts such as `../../etc/passwd` are blocked at this layer.
- Scan optimizations in `sync_service.py` call `find` through
`asyncio.create_subprocess_exec()` with explicit argument lists. Project paths
are passed as data, not interpolated into shell strings.
- Auto-update code uses hardcoded commands, list-form arguments, and
`stdin=DEVNULL`. User-controlled strings do not reach a shell there.
### MCP Client-Side Risk
Recent MCP ecosystem research has highlighted a client-side pattern where an
MCP host can be configured to run arbitrary commands as "servers." That risk is
in the host configuration, not in notes or Basic Memory tool input.
The recommended Basic Memory MCP configuration uses a known command with
explicit arguments:
```json
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": ["basic-memory", "mcp"]
}
}
}
```
Only add MCP server entries from sources you trust. Avoid inline shell scripts
or command strings copied from untrusted sources. Treat third-party MCP server
configuration with the same scrutiny as any locally executed program.
Related ecosystem context:
- OX Security: The Mother of All AI Supply Chains
- CSO Online: RCE by design: MCP architectural choice haunts AI agent ecosystem
### Out Of Scope
- Basic Memory does not execute note content as code. Notes are returned as
data to the LLM.
- Basic Memory does not open network ports by default. The MCP server uses
stdio; the optional REST API is intended for localhost use.
- Basic Memory is designed for single-user local knowledge bases and does not
implement access controls between operating-system users.
## Secure Configuration Checklist
- MCP config `command` points to `uvx` or a trusted binary, not a shell string.
- Project paths in Basic Memory config come from trusted local configuration.
- If exposing the REST API, bind it only to localhost.
- Review any third-party MCP servers before adding them to your host config.
+1419
View File
File diff suppressed because it is too large Load Diff
-45
View File
@@ -1,45 +0,0 @@
# Docker Compose configuration for Basic Memory with PostgreSQL
# Use this for local development and testing with Postgres backend.
#
# The Postgres backend requires the pgvector extension (semantic search).
# This image bundles pgvector; plain postgres:17 will not work for vector search.
#
# Usage:
# docker-compose -f docker-compose-postgres.yml up -d
# docker-compose -f docker-compose-postgres.yml down
services:
postgres:
image: pgvector/pgvector:pg17
container_name: basic-memory-postgres
environment:
# Local development/test credentials - NOT for production
# These values are referenced by tests and justfile commands
POSTGRES_DB: basic_memory
POSTGRES_USER: basic_memory_user
POSTGRES_PASSWORD: dev_password # Simple password for local testing only
ports:
- "5433:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U basic_memory_user -d basic_memory"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
# Named volume for Postgres data
postgres_data:
driver: local
# Named volume for persistent configuration
# Database will be stored in Postgres, not in this volume
basic-memory-config:
driver: local
# Network configuration (optional)
# networks:
# basic-memory-net:
# driver: bridge
-85
View File
@@ -1,85 +0,0 @@
# Docker Compose configuration for Basic Memory
# See docs/Docker.md for detailed setup instructions
version: '3.8'
services:
basic-memory:
# Use pre-built image (recommended for most users)
image: ghcr.io/basicmachines-co/basic-memory:latest
# Uncomment to build locally instead:
# build: .
container_name: basic-memory-server
# Volume mounts for knowledge directories and persistent data
volumes:
# Persistent storage for configuration and database
# Container runs as `appuser` (Dockerfile USER directive), so the CLI
# config dir lives under /home/appuser, not /root.
- basic-memory-config:/home/appuser/.basic-memory:rw
# Mount your knowledge directory (required)
# Change './knowledge' to your actual Obsidian vault or knowledge directory
- ./knowledge:/app/data:rw
# OPTIONAL: Mount additional knowledge directories for multiple projects
# - ./work-notes:/app/data/work:rw
# - ./personal-notes:/app/data/personal:rw
# You can edit the project config manually in the mounted config volume
# The default project will be configured to use /app/data
environment:
# Project configuration
- BASIC_MEMORY_DEFAULT_PROJECT=main
# Enable real-time file synchronization (recommended for Docker)
- BASIC_MEMORY_SYNC_CHANGES=true
# Logging configuration
- BASIC_MEMORY_LOG_LEVEL=INFO
# Sync delay in milliseconds (adjust for performance vs responsiveness)
- BASIC_MEMORY_SYNC_DELAY=1000
# Port exposure for HTTP transport (only needed if not using STDIO)
ports:
- "8000:8000"
# Command with SSE transport (configurable via environment variables above)
# IMPORTANT: The SSE and streamable-http endpoints are not secured
command: ["basic-memory", "mcp", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]
# Container management
restart: unless-stopped
# Health monitoring
healthcheck:
test: ["CMD", "basic-memory", "--version"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# Optional: Resource limits
# deploy:
# resources:
# limits:
# memory: 512M
# cpus: '0.5'
# reservations:
# memory: 256M
# cpus: '0.25'
volumes:
# Named volume for persistent configuration and database
# This ensures your configuration and knowledge graph persist across container restarts
basic-memory-config:
driver: local
# Network configuration (optional)
# networks:
# basic-memory-net:
# driver: bridge
+1
View File
@@ -0,0 +1 @@
{}
+31
View File
@@ -0,0 +1,31 @@
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": true,
"canvas": true,
"outgoing-link": true,
"tag-pane": true,
"properties": false,
"page-preview": true,
"daily-notes": true,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": false,
"editor-status": true,
"bookmarks": true,
"markdown-importer": false,
"zk-prefixer": false,
"random-note": false,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": false,
"file-recovery": true,
"publish": true,
"sync": true,
"webviewer": false
}
+6
View File
@@ -0,0 +1,6 @@
{
"siteId": "947ee055a8c6f1a57efa4afa09791e62",
"host": "publish-01.obsidian.md",
"included": [],
"excluded": []
}
+396
View File
@@ -0,0 +1,396 @@
---
title: AI Assistant Guide
type: note
permalink: docs/ai-assistant-guide
---
> Note: This is an optional document that can be copy/pasted into the project knowledge for an LLM to provide a full description of how it can work with Basic Memory. It is provided as a helpful resource. The tools contain extensive usage description prompts with enable the LLM to understand them.
# AI Assistant Guide for Basic Memory
This guide helps you, the AI assistant, use Basic Memory tools effectively when working with users. It covers reading, writing, and navigating knowledge through the Model Context Protocol (MCP).
## Overview
Basic Memory allows you and users to record context in local Markdown files, building a rich knowledge base through natural conversations. The system automatically creates a semantic knowledge graph from simple text patterns.
- **Local-First**: All data is stored in plain text files on the user's computer
- **Real-Time**: Users see content updates immediately
- **Bi-Directional**: Both you and users can read and edit notes
- **Semantic**: Simple patterns create a structured knowledge graph
- **Persistent**: Knowledge persists across sessions and conversations
## The Importance of the Knowledge Graph
Basic Memory's value comes from connections between notes, not just the notes themselves. When writing notes, your primary goal should be creating a rich, interconnected knowledge graph.
When creating content, focus on:
1. **Increasing Semantic Density**: Add multiple observations and relations to each note
2. **Using Accurate References**: Aim to reference existing entities by their exact titles
3. **Creating Forward References**: Feel free to reference entities that don't exist yet - Basic Memory will resolve these when they're created later
4. **Creating Bidirectional Links**: When appropriate, connect entities from both directions
5. **Using Meaningful Categories**: Add semantic context with appropriate observation categories
6. **Choosing Precise Relations**: Use specific relation types that convey meaning
Remember that a knowledge graph with 10 heavily connected notes is more valuable than 20 isolated notes. Your job is to help build these connections.
## Core Tools Reference
```python
# Writing knowledge - THE MOST IMPORTANT TOOL!
response = await write_note(
title="Search Design", # Required: Note title
content="# Search Design\n...", # Required: Note content
folder="specs", # Optional: Folder to save in
tags=["search", "design"], # Optional: Tags for categorization
verbose=True # Optional: Get parsing details
)
# Reading knowledge
content = await read_note("Search Design") # By title
content = await read_note("specs/search-design") # By path
content = await read_note("memory://specs/search") # By memory URL
# Searching for knowledge
results = await search(
query="authentication system", # Text to search for
page=1, # Optional: Pagination
page_size=10 # Optional: Results per page
)
# Building context from the knowledge graph
context = await build_context(
url="memory://specs/search", # Starting point
depth=2, # Optional: How many hops to follow
timeframe="1 month" # Optional: Recent timeframe
)
# Checking recent changes
activity = await recent_activity(
type="all", # Optional: Entity types to include
depth=1, # Optional: Related items to include
timeframe="1 week" # Optional: Time window
)
# Creating a knowledge visualization
canvas_result = await canvas(
nodes=[{"id": "note1", "label": "Search Design"}], # Nodes to display
edges=[{"from": "note1", "to": "note2"}], # Connections
title="Project Overview", # Canvas title
folder="diagrams" # Storage location
)
```
## memory:// URLs Explained
Basic Memory uses a special URL format to reference entities in the knowledge graph:
- `memory://title` - Reference by title
- `memory://folder/title` - Reference by folder and title
- `memory://permalink` - Reference by permalink
- `memory://path/relation_type/*` - Follow all relations of a specific type
- `memory://path/*/target` - Find all entities with relations to target
## Semantic Markdown Format
Knowledge is encoded in standard markdown using simple patterns:
**Observations** - Facts about an entity:
```markdown
- [category] This is an observation #tag1 #tag2 (optional context)
```
**Relations** - Links between entities:
```markdown
- relation_type [[Target Entity]] (optional context)
```
**Common Categories & Relation Types:**
- Categories: `[idea]`, `[decision]`, `[question]`, `[fact]`, `[requirement]`, `[technique]`, `[recipe]`, `[preference]`
- Relations: `relates_to`, `implements`, `requires`, `extends`, `part_of`, `pairs_with`, `inspired_by`, `originated_from`
## When to Record Context
**Always consider recording context when**:
1. Users make decisions or reach conclusions
2. Important information emerges during conversation
3. Multiple related topics are discussed
4. The conversation contains information that might be useful later
5. Plans, tasks, or action items are mentioned
**Protocol for recording context**:
1. Identify valuable information in the conversation
2. Ask the user: "Would you like me to record our discussion about [topic] in Basic Memory?"
3. If they agree, use `write_note` to capture the information
4. If they decline, continue without recording
5. Let the user know when information has been recorded: "I've saved our discussion about [topic] to Basic Memory."
## Understanding User Interactions
Users will interact with Basic Memory in patterns like:
1. **Creating knowledge**:
```
Human: "Let's write up what we discussed about search."
You: I'll create a note capturing our discussion about the search functionality.
[Use write_note() to record the conversation details]
```
2. **Referencing existing knowledge**:
```
Human: "Take a look at memory://specs/search"
You: I'll examine that information.
[Use build_context() to gather related information]
[Then read_note() to access specific content]
```
3. **Finding information**:
```
Human: "What were our decisions about auth?"
You: Let me find that information for you.
[Use search() to find relevant notes]
[Then build_context() to understand connections]
```
## Key Things to Remember
1. **Files are Truth**
- All knowledge lives in local files on the user's computer
- Users can edit files outside your interaction
- Changes need to be synced by the user (usually automatic)
- Always verify information is current with `recent_activity()`
2. **Building Context Effectively**
- Start with specific entities
- Follow meaningful relations
- Check recent changes
- Build context incrementally
- Combine related information
3. **Writing Knowledge Wisely**
- Using the same title+folder will overwrite existing notes
- Structure content with clear headings and sections
- Use semantic markup for observations and relations
- Keep files organized in logical folders
## Common Knowledge Patterns
### Capturing Decisions
```markdown
# Coffee Brewing Methods
## Context
I've experimented with various brewing methods including French press, pour over, and espresso.
## Decision
Pour over is my preferred method for light to medium roasts because it highlights subtle flavors and offers more control over the extraction.
## Observations
- [technique] Blooming the coffee grounds for 30 seconds improves extraction #brewing
- [preference] Water temperature between 195-205°F works best #temperature
- [equipment] Gooseneck kettle provides better control of water flow #tools
## Relations
- pairs_with [[Light Roast Beans]]
- contrasts_with [[French Press Method]]
- requires [[Proper Grinding Technique]]
```
### Recording Project Structure
```markdown
# Garden Planning
## Overview
This document outlines the garden layout and planting strategy for this season.
## Observations
- [structure] Raised beds in south corner for sun exposure #layout
- [structure] Drip irrigation system installed for efficiency #watering
- [pattern] Companion planting used to deter pests naturally #technique
## Relations
- contains [[Vegetable Section]]
- contains [[Herb Garden]]
- implements [[Organic Gardening Principles]]
```
### Technical Discussions
```markdown
# Recipe Improvement Discussion
## Key Points
Discussed strategies for improving the chocolate chip cookie recipe.
## Observations
- [issue] Cookies spread too thin when baked at 350°F #texture
- [solution] Chilling dough for 24 hours improves flavor and reduces spreading #technique
- [decision] Will use brown butter instead of regular butter #flavor
## Relations
- improves [[Basic Cookie Recipe]]
- inspired_by [[Bakery-Style Cookies]]
- pairs_with [[Homemade Ice Cream]]
```
### Creating Effective Relations
When creating relations, you can:
1. Reference existing entities by their exact title
2. Create forward references to entities that don't exist yet
```python
# Example workflow for creating notes with effective relations
async def create_note_with_effective_relations():
# Search for existing entities to reference
search_results = await search("travel")
existing_entities = [result.title for result in search_results.primary_results]
# Check if specific entities exist
packing_tips_exists = "Packing Tips" in existing_entities
japan_travel_exists = "Japan Travel Guide" in existing_entities
# Prepare relations section - include both existing and forward references
relations_section = "## Relations\n"
# Existing reference - exact match to known entity
if packing_tips_exists:
relations_section += "- references [[Packing Tips]]\n"
else:
# Forward reference - will be linked when that entity is created later
relations_section += "- references [[Packing Tips]]\n"
# Another possible reference
if japan_travel_exists:
relations_section += "- part_of [[Japan Travel Guide]]\n"
# You can also check recently modified notes to reference them
recent = await recent_activity(timeframe="1 week")
recent_titles = [item.title for item in recent.primary_results]
if "Transportation Options" in recent_titles:
relations_section += "- relates_to [[Transportation Options]]\n"
# Always include meaningful forward references, even if they don't exist yet
relations_section += "- located_in [[Tokyo]]\n"
relations_section += "- visited_during [[Spring 2023 Trip]]\n"
# Now create the note with both verified and forward relations
content = f"""# Tokyo Neighborhood Guide
## Overview
Details about different Tokyo neighborhoods and their unique characteristics.
## Observations
- [area] Shibuya is a busy shopping district #shopping
- [transportation] Yamanote Line connects major neighborhoods #transit
- [recommendation] Visit Shimokitazawa for vintage shopping #unique
- [tip] Get a Suica card for easy train travel #convenience
{relations_section}
"""
result = await write_note(
title="Tokyo Neighborhood Guide",
content=content,
verbose=True
)
# You can check which relations were resolved and which are forward references
if result and 'relations' in result:
resolved = [r['to_name'] for r in result['relations'] if r.get('target_id')]
forward_refs = [r['to_name'] for r in result['relations'] if not r.get('target_id')]
print(f"Resolved relations: {resolved}")
print(f"Forward references that will be resolved later: {forward_refs}")
```
## Error Handling
Common issues to watch for:
1. **Missing Content**
```python
try:
content = await read_note("Document")
except:
# Try search instead
results = await search("Document")
if results and results.primary_results:
# Found something similar
content = await read_note(results.primary_results[0].permalink)
```
2. **Forward References (Unresolved Relations)**
```python
response = await write_note(..., verbose=True)
# Check for forward references (unresolved relations)
forward_refs = []
for relation in response.get('relations', []):
if not relation.get('target_id'):
forward_refs.append(relation.get('to_name'))
if forward_refs:
# This is a feature, not an error! Inform the user about forward references
print(f"Note created with forward references to: {forward_refs}")
print("These will be automatically linked when those notes are created.")
# Optionally suggest creating those entities now
print("Would you like me to create any of these notes now to complete the connections?")
```
3. **Sync Issues**
```python
# If information seems outdated
activity = await recent_activity(timeframe="1 hour")
if not activity or not activity.primary_results:
print("It seems there haven't been recent updates. You might need to run 'basic-memory sync'.")
```
## Best Practices
1. **Proactively Record Context**
- Offer to capture important discussions
- Record decisions, rationales, and conclusions
- Link to related topics
- Ask for permission first: "Would you like me to save our discussion about [topic]?"
- Confirm when complete: "I've saved our discussion to Basic Memory"
2. **Create a Rich Semantic Graph**
- **Add meaningful observations**: Include at least 3-5 categorized observations in each note
- **Create deliberate relations**: Connect each note to at least 2-3 related entities
- **Use existing entities**: Before creating a new relation, search for existing entities
- **Verify wikilinks**: When referencing `[[Entity]]`, use exact titles of existing notes
- **Check accuracy**: Use `search()` or `recent_activity()` to confirm entity titles
- **Use precise relation types**: Choose specific relation types that convey meaning (e.g., "implements" instead of "relates_to")
- **Consider bidirectional relations**: When appropriate, create inverse relations in both entities
3. **Structure Content Thoughtfully**
- Use clear, descriptive titles
- Organize with logical sections (Context, Decision, Implementation, etc.)
- Include relevant context and background
- Add semantic observations with appropriate categories
- Use a consistent format for similar types of notes
- Balance detail with conciseness
4. **Navigate Knowledge Effectively**
- Start with specific searches
- Follow relation paths
- Combine information from multiple sources
- Verify information is current
- Build a complete picture before responding
5. **Help Users Maintain Their Knowledge**
- Suggest organizing related topics
- Identify potential duplicates
- Recommend adding relations between topics
- Offer to create summaries of scattered information
- Suggest potential missing relations: "I notice this might relate to [topic], would you like me to add that connection?"
Built with ♥️ by Basic Machines
-442
View File
@@ -1,442 +0,0 @@
# Basic Memory Architecture
This document describes the architectural patterns and composition structure of Basic Memory.
## Overview
Basic Memory is a local-first knowledge management system with three entrypoints:
- **API** - FastAPI REST server for HTTP access
- **MCP** - Model Context Protocol server for LLM integration
- **CLI** - Typer command-line interface
Each entrypoint uses a **composition root** pattern to manage configuration and dependencies.
## Composition Roots
### What is a Composition Root?
A composition root is the single place in an application where dependencies are wired together. In Basic Memory, each entrypoint has its own composition root that:
1. Reads configuration from `ConfigManager`
2. Resolves runtime mode (local/test)
3. Creates and provides dependencies to downstream code
**Key principle**: Only composition roots read global configuration. All other modules receive configuration explicitly.
### Container Structure
Each entrypoint has a container dataclass in its package:
```
src/basic_memory/
├── api/
│ └── container.py # ApiContainer
├── mcp/
│ └── container.py # McpContainer
├── cli/
│ └── container.py # CliContainer
└── runtime.py # RuntimeMode enum and resolver
```
### Container Pattern
All containers follow the same structure:
```python
@dataclass
class Container:
config: BasicMemoryConfig
mode: RuntimeMode
@classmethod
def create(cls) -> "Container":
"""Create container by reading ConfigManager."""
config = ConfigManager().config
mode = resolve_runtime_mode(is_test_env=config.is_test_env)
return cls(config=config, mode=mode)
@property
def some_computed_property(self) -> bool:
"""Derived values based on config and mode."""
return self.mode.is_local and self.config.some_setting
# Module-level singleton
_container: Container | None = None
def get_container() -> Container:
if _container is None:
raise RuntimeError("Container not initialized")
return _container
def set_container(container: Container) -> None:
global _container
_container = container
```
### Runtime Mode Resolution
The `RuntimeMode` enum centralizes mode detection:
```python
class RuntimeMode(Enum):
LOCAL = "local"
CLOUD = "cloud"
TEST = "test"
@property
def is_cloud(self) -> bool:
return self == RuntimeMode.CLOUD
@property
def is_local(self) -> bool:
return self == RuntimeMode.LOCAL
@property
def is_test(self) -> bool:
return self == RuntimeMode.TEST
```
Resolution follows this precedence in local app flows: **TEST > LOCAL**
```python
def resolve_runtime_mode(is_test_env: bool) -> RuntimeMode:
if is_test_env:
return RuntimeMode.TEST
return RuntimeMode.LOCAL
```
**Note**: `RuntimeMode` determines global behavior (e.g., whether to start file sync).
Per-project routing is orthogonal: individual projects can be set to `cloud` mode via `ProjectMode`,
which affects client routing in `get_client(project_name=...)` without changing global runtime mode.
`RuntimeMode.CLOUD` may remain for compatibility, but standard local runtime resolution does not select it.
## Dependencies Package
### Structure
The `deps/` package provides FastAPI dependencies organized by feature:
```
src/basic_memory/deps/
├── __init__.py # Re-exports for backwards compatibility
├── config.py # Configuration access
├── db.py # Database/session management
├── projects.py # Project resolution
├── repositories.py # Data access layer
├── services.py # Business logic layer
└── importers.py # Import functionality
```
### Usage in Routers
```python
from basic_memory.deps.services import get_entity_service
from basic_memory.deps.projects import get_project_config
@router.get("/entities/{id}")
async def get_entity(
id: int,
entity_service: EntityService = Depends(get_entity_service),
project: ProjectConfig = Depends(get_project_config),
):
return await entity_service.get(id)
```
### Backwards Compatibility
The old `deps.py` file still exists as a thin re-export shim:
```python
# deps.py - backwards compatibility shim
from basic_memory.deps import *
```
New code should import from specific submodules (`basic_memory.deps.services`) for clarity.
## MCP Tools Architecture
### Typed API Clients
MCP tools communicate with the API through typed clients that encapsulate HTTP paths and response validation:
```
src/basic_memory/mcp/clients/
├── __init__.py # Re-exports all clients
├── base.py # BaseClient with common logic
├── knowledge.py # KnowledgeClient - entity CRUD
├── search.py # SearchClient - search operations
├── memory.py # MemoryClient - context building
├── directory.py # DirectoryClient - directory listing
├── resource.py # ResourceClient - resource reading
└── project.py # ProjectClient - project management
```
### Client Pattern
Each client encapsulates API paths and validates responses:
```python
class KnowledgeClient(BaseClient):
"""Client for knowledge/entity operations."""
async def resolve_entity(self, identifier: str) -> int:
"""Resolve identifier to entity ID."""
response = await call_get(
self.http_client,
f"{self._base_path}/resolve/{identifier}",
)
return int(response.text)
async def get_entity(self, entity_id: int) -> EntityResponse:
"""Get entity by ID."""
response = await call_get(
self.http_client,
f"{self._base_path}/entities/{entity_id}",
)
return EntityResponse.model_validate(response.json())
```
### Tool → Client → API Flow
```
MCP Tool (thin adapter)
Typed Client (encapsulates paths, validates responses)
HTTP API (FastAPI router)
Service Layer (business logic)
Repository Layer (data access)
```
Example tool using typed client:
```python
@mcp.tool()
async def search_notes(
query: str,
project: str | None = None,
metadata_filters: dict | None = None,
tags: list[str] | None = None,
status: str | None = None,
) -> SearchResponse:
async with get_project_client(project, context) as (client, active_project):
# Import client inside function to avoid circular imports
from basic_memory.mcp.clients import SearchClient
from basic_memory.schemas.search import SearchQuery
search_query = SearchQuery(
text=query,
metadata_filters=metadata_filters,
tags=tags,
status=status,
)
search_client = SearchClient(client, active_project.external_id)
return await search_client.search(search_query.model_dump())
```
### Per-Project Client Routing
`get_project_client()` from `mcp/project_context.py` is an async context manager that:
1. Resolves the project name from config (no network call)
2. Creates the correctly-routed client based on the project's mode (local ASGI or cloud HTTP with API key)
3. Validates the project via the API
4. Yields `(client, active_project)` tuple
This solves the bootstrap problem: you need the project name to choose the right client (local vs cloud), but you need the client to validate the project exists.
```python
from basic_memory.mcp.project_context import get_project_client
async with get_project_client(project, context) as (client, active_project):
# client is routed based on project's mode (local or cloud)
# active_project is validated via the API
...
```
## Sync Coordination
### SyncCoordinator
The `SyncCoordinator` centralizes sync/watch lifecycle management:
```python
@dataclass
class SyncCoordinator:
"""Coordinates file sync and watch operations."""
status: SyncStatus = SyncStatus.NOT_STARTED
sync_task: asyncio.Task | None = None
watch_service: WatchService | None = None
async def start(self, ...):
"""Start sync and watch operations."""
async def stop(self):
"""Stop all sync operations gracefully."""
def get_status_info(self) -> dict:
"""Get current sync status for observability."""
```
### Status Enum
```python
class SyncStatus(Enum):
NOT_STARTED = "not_started"
STARTING = "starting"
RUNNING = "running"
STOPPING = "stopping"
STOPPED = "stopped"
ERROR = "error"
```
## Project Resolution
### ProjectResolver
Unified project selection across all entrypoints:
```python
class ProjectResolver:
"""Resolves which project to use based on context."""
def resolve(
self,
explicit_project: str | None = None,
) -> ResolvedProject:
"""Resolve project using three-tier hierarchy:
1. Explicit project parameter
2. Default project from config
3. Single available project
"""
```
### Resolution Modes
```python
class ResolutionMode(Enum):
EXPLICIT = "explicit" # User specified project
DEFAULT = "default" # Using configured default
SINGLE_PROJECT = "single" # Only one project exists
FALLBACK = "fallback" # Using first available
```
## Testing Patterns
### Container Testing
Each container has corresponding tests:
```
tests/
├── api/test_api_container.py
├── mcp/test_mcp_container.py
└── cli/test_cli_container.py
```
Tests verify:
- Container creation from config
- Runtime mode properties
- Container accessor functions (get/set)
### Mocking Typed Clients
When testing MCP tools, mock at the client level:
```python
def test_search_notes(monkeypatch):
import basic_memory.mcp.clients as clients_mod
class MockSearchClient:
async def search(self, query):
return SearchResponse(results=[...])
monkeypatch.setattr(clients_mod, "SearchClient", MockSearchClient)
```
## Design Principles
### 1. Explicit Dependencies
Modules receive configuration explicitly rather than reading globals:
```python
# Good - explicit injection
async def sync_files(config: BasicMemoryConfig):
...
# Avoid - hidden global access
async def sync_files():
config = ConfigManager().config # Hidden coupling
```
### 2. Single Responsibility
Each layer has a clear responsibility:
- **Containers**: Wire dependencies
- **Clients**: Encapsulate HTTP communication
- **Services**: Business logic
- **Repositories**: Data access
- **Tools/Routers**: Thin adapters
### 3. Deferred Imports
To avoid circular imports, typed clients are imported inside functions:
```python
async def my_tool():
async with get_client() as client:
# Import here to avoid circular dependency
from basic_memory.mcp.clients import KnowledgeClient
knowledge_client = KnowledgeClient(client, project_id)
```
### 4. Backwards Compatibility
When refactoring, maintain backwards compatibility via shims:
```python
# Old module becomes a shim
from basic_memory.new_location import *
# Docstring explains migration path
"""
DEPRECATED: Import from basic_memory.new_location instead.
This shim will be removed in a future version.
"""
```
## File Organization
```
src/basic_memory/
├── api/
│ ├── container.py # API composition root
│ ├── routers/ # FastAPI routers
│ └── ...
├── mcp/
│ ├── container.py # MCP composition root
│ ├── clients/ # Typed API clients
│ ├── tools/ # MCP tool definitions
│ └── server.py # MCP server setup
├── cli/
│ ├── container.py # CLI composition root
│ ├── app.py # Typer app
│ └── commands/ # CLI command groups
├── deps/
│ ├── config.py # Config dependencies
│ ├── db.py # Database dependencies
│ ├── projects.py # Project dependencies
│ ├── repositories.py # Repository dependencies
│ ├── services.py # Service dependencies
│ └── importers.py # Importer dependencies
├── sync/
│ ├── coordinator.py # SyncCoordinator
│ └── ...
├── runtime.py # RuntimeMode resolution
├── project_resolver.py # Unified project selection
└── config.py # Configuration management
```
+289
View File
@@ -0,0 +1,289 @@
---
title: CLI Reference
type: note
permalink: docs/cli-reference
---
# CLI Reference
Basic Memory provides command line tools for managing your knowledge base. This reference covers the available commands and their options.
## Core Commands
### sync
Keeps files and the knowledge graph in sync:
```bash
# Basic sync
basic-memory sync
# Watch for changes
basic-memory sync --watch
# Sync specific folder
basic-memory sync path/to/folder
```
Options:
- `--watch`: Continuously monitor for changes
- `--verbose`: Show detailed output
- `PATH`: Optional path to sync (defaults to ~/basic-memory)
### import
Imports external knowledge sources:
```bash
# Claude conversations
basic-memory import claude conversations
# Claude projects
basic-memory import claude projects
# ChatGPT history
basic-memory import chatgpt
```
Options:
- `--folder PATH`: Target folder for imported content
- `--overwrite`: Replace existing files
- `--skip-existing`: Keep existing files
### status
Shows system status information:
```bash
# Basic status check
basic-memory status
# Detailed status
basic-memory status --verbose
# JSON output
basic-memory status --json
```
### project
Create multiple projects to manage your knowledge.
```bash
# List all configured projects
basic-memory project list
# Add a new project
basic-memory project add work ~/work-basic-memory
# Set the default project
basic-memory project default work
# Remove a project (doesn't delete files)
basic-memory project remove personal
# Show current project
basic-memory project current
```
> Be sure to restart Claude Desktop after changing projects.
#### Using Projects in Commands
All commands support the `--project` flag to specify which project to use:
```bash
# Sync a specific project
basic-memory --project=work sync
# Run MCP server for a specific project
basic-memory --project=personal mcp
```
You can also set the `BASIC_MEMORY_PROJECT` environment variable:
```bash
BASIC_MEMORY_PROJECT=work basic-memory sync
```
### help
The full list of commands and help for each can be viewed with the `--help` argument.
```
✗ basic-memory --help
Usage: basic-memory [OPTIONS] COMMAND [ARGS]...
Basic Memory - Local-first personal knowledge management system.
╭─ Options ─────────────────────────────────────────────────────────────────────────────────╮
│ --project -p TEXT Specify which project to use │
│ [env var: BASIC_MEMORY_PROJECT] │
│ [default: None] │
│ --version -V Show version information and exit. │
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to copy it or │
│ customize the installation. │
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────────────────╮
│ sync Sync knowledge files with the database. │
│ status Show sync status between files and database. │
│ reset Reset database (drop all tables and recreate). │
│ mcp Run the MCP server for Claude Desktop integration. │
│ import Import data from various sources │
│ tool Direct access to MCP tools via CLI │
│ project Manage multiple Basic Memory projects │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
```
## Initial Setup
```bash
# Install Basic Memory
uv install basic-memory
# First sync
basic-memory sync
# Start watching mode
basic-memory sync --watch
```
> **Important**: You need to install Basic Memory via `uv` or `pip` to use the command line tools, see [[Getting Started with Basic Memory#Installation]].
## Regular Usage
```bash
# Check status
basic-memory status
# Import new content
basic-memory import claude conversations
# Sync changes
basic-memory sync
# Sync changes continuously
basic-memory sync --watch
```
## Maintenance Tasks
```bash
# Check system status in detail
basic-memory status --verbose
# Full resync of all files
basic-memory sync
# Import updates to specific folder
basic-memory import claude conversations --folder new
```
## Using stdin with Basic Memory's `write_note` Tool
The `write-note` tool supports reading content from standard input (stdin), allowing for more flexible workflows when creating or updating notes in your Basic Memory knowledge base.
### Use Cases
This feature is particularly useful for:
1. **Piping output from other commands** directly into Basic Memory notes
2. **Creating notes with multi-line content** without having to escape quotes or special characters
3. **Integrating with AI assistants** like Claude Code that can generate content and pipe it to Basic Memory
4. **Processing text data** from files or other sources
### Basic Usage
#### Method 1: Using a Pipe
You can pipe content from another command into `write_note`:
```bash
# Pipe output of a command into a new note
echo "# My Note\n\nThis is a test note" | basic-memory tool write-note --title "Test Note" --folder "notes"
# Pipe output of a file into a new note
cat README.md | basic-memory tool write-note --title "Project README" --folder "documentation"
# Process text through other tools before saving as a note
cat data.txt | grep "important" | basic-memory tool write-note --title "Important Data" --folder "data"
```
#### Method 2: Using Heredoc Syntax
For multi-line content, you can use heredoc syntax:
```bash
# Create a note with heredoc
cat << EOF | basic-memory tool write_note --title "Project Ideas" --folder "projects"
# Project Ideas for Q2
## AI Integration
- Improve recommendation engine
- Add semantic search to product catalog
## Infrastructure
- Migrate to Kubernetes
- Implement CI/CD pipeline
EOF
```
#### Method 3: Input Redirection
You can redirect input from a file:
```bash
# Create a note from file content
basic-memory tool write-note --title "Meeting Notes" --folder "meetings" < meeting_notes.md
```
#### Integration with Claude Code
This feature works well with Claude Code in the terminal:
In a Claude Code session, let Claude know he can use the basic-memory tools, then he can execute them via the cli:
```
⏺ Bash(echo "# Test Note from Claude\n\nThis is a test note created by Claude to test the stdin functionality." | basic-memory tool write-note --title "Claude Test Note" --folder "test" --tags "test" --tags "claude")…
  ⎿  # Created test/Claude Test Note.md (23e00eec)
permalink: test/claude-test-note
## Tags
- test, claude
```
## Troubleshooting Common Issues
### Sync Conflicts
If you encounter a file changed during sync error:
1. Check the file referenced in the error message
2. Resolve any conflicts manually
3. Run sync again
### Import Errors
If import fails:
1. Check that the source file is in the correct format
2. Verify permissions on the target directory
3. Use --verbose flag for detailed error information
### Status Issues
If status shows problems:
1. Note any unresolved relations or warnings
2. Run a full sync to attempt automatic resolution
3. Check file permissions if database access errors occur
## Relations
- used_by [[Getting Started with Basic Memory]] (Installation instructions)
- complements [[User Guide]] (How to use Basic Memory)
- relates_to [[Introduction to Basic Memory]] (System overview)
+107
View File
@@ -0,0 +1,107 @@
---
title: Canvas Visualizations
type: note
permalink: docs/canvas
tags:
- visualization
- mapping
- obsidian
---
# Canvas Visualizations
Basic Memory can create visual knowledge maps using Obsidian's Canvas feature. These visualizations help you understand relationships between concepts, map out processes, and visualize your knowledge structure.
## Creating Canvas Visualizations
Ask Claude to create a visualization by describing what you want to map:
```
You: "Create a canvas visualization of my project components and their relationships."
You: "Make a concept map showing the main themes from our discussion about climate change."
You: "Can you make a canvas diagram of the perfect pour over method?"
```
![[Canvas.png]]
## Types of Visualizations
Basic Memory can create several types of visual maps:
### Document Maps
Visualize connections between your notes and documents
### Concept Maps
Create visual representations of ideas and their relationships
### Process Diagrams
Map workflows, sequences, and procedures
### Thematic Analysis
Organize ideas around central themes
### Relationship Networks
Show how different entities relate to each other
## Visualization Sources
Claude can create visualizations based on:
### Documents in Your Knowledge Base
```
You: "Create a canvas showing the connections between my project planning documents"
```
### Conversation Content
```
You: "Make a canvas visualization of the main points we just discussed"
```
### Search Results
```
You: "Find all my notes about psychology and create a visual map of the concepts"
```
### Themes and Relationships
```
You: "Create a visual map showing how different philosophical schools relate to each other"
```
## Visualization Workflow
1. **Request a visualization** by describing what you want to see
2. **Claude creates the canvas file** in your Basic Memory directory
3. **Open the file in Obsidian** to view the visualization
4. **Refine the visualization** by asking Claude for adjustments:
```
You: "Could you reorganize the canvas to group related components together?"
You: "Please add more detail about the connection between these two concepts."
```
## Technical Details
Behind the scenes, Claude:
1. Creates a `.canvas` file in JSON format
2. Adds nodes for each concept or document
3. Creates edges to represent relationships
4. Sets positions for visual clarity
5. Includes any relevant metadata
The resulting file is fully compatible with Obsidian's Canvas feature and can be edited directly in Obsidian.
## Tips for Effective Visualizations
- **Be specific** about what you want to visualize
- **Specify the level of detail** you need
- **Mention the visualization type** you want (concept map, process flow, etc.)
- **Start simple** and ask for refinements
- **Provide context** about what documents or concepts to include
## Relations
- enhances [[Obsidian Integration]] (Using Basic Memory with Obsidian)
- visualizes [[Knowledge Format]] (The structure of your knowledge)
- complements [[User Guide]] (Ways to use Basic Memory)
-365
View File
@@ -1,365 +0,0 @@
# Docker Setup Guide
Basic Memory can be run in Docker containers to provide a consistent, isolated environment for your knowledge management
system. This is particularly useful for integrating with existing Dockerized MCP servers or for deployment scenarios.
## Quick Start
### Option 1: Using Pre-built Images (Recommended)
Basic Memory provides pre-built Docker images on GitHub Container Registry that are automatically updated with each release.
1. **Use the official image directly:**
```bash
docker run -d \
--name basic-memory-server \
-p 8000:8000 \
-v /path/to/your/obsidian-vault:/app/data:rw \
-v basic-memory-config:/app/.basic-memory:rw \
ghcr.io/basicmachines-co/basic-memory:latest
```
2. **Or use Docker Compose with the pre-built image:**
```yaml
version: '3.8'
services:
basic-memory:
image: ghcr.io/basicmachines-co/basic-memory:latest
container_name: basic-memory-server
ports:
- "8000:8000"
volumes:
- /path/to/your/obsidian-vault:/app/data:rw
- basic-memory-config:/app/.basic-memory:rw
environment:
- BASIC_MEMORY_DEFAULT_PROJECT=main
restart: unless-stopped
```
### Option 2: Using Docker Compose (Building Locally)
1. **Clone the repository:**
```bash
git clone https://github.com/basicmachines-co/basic-memory.git
cd basic-memory
```
2. **Update the docker-compose.yml:**
Edit the volume mount to point to your Obsidian vault:
```yaml
volumes:
# Change './obsidian-vault' to your actual directory path
- /path/to/your/obsidian-vault:/app/data:rw
```
3. **Start the container:**
```bash
docker-compose up -d
```
### Option 3: Using Docker CLI
```bash
# Build the image
docker build -t basic-memory .
# Run with volume mounting
docker run -d \
--name basic-memory-server \
-v /path/to/your/obsidian-vault:/app/data:rw \
-v basic-memory-config:/app/.basic-memory:rw \
-e BASIC_MEMORY_DEFAULT_PROJECT=main \
basic-memory
```
## Configuration
### Volume Mounts
Basic Memory requires several volume mounts for proper operation:
1. **Knowledge Directory** (Required):
```yaml
- /path/to/your/obsidian-vault:/app/data:rw
```
Mount your Obsidian vault or knowledge base directory.
2. **Configuration and Database** (Recommended):
```yaml
- basic-memory-config:/app/.basic-memory:rw
```
Persistent storage for configuration and SQLite database.
You can edit the basic-memory config.json file located in the /app/.basic-memory/config.json after Basic Memory starts.
3. **Multiple Projects** (Optional):
```yaml
- /path/to/project1:/app/data/project1:rw
- /path/to/project2:/app/data/project2:rw
```
You can edit the basic-memory config.json file located in the /app/.basic-memory/config.json
## CLI Commands via Docker
You can run Basic Memory CLI commands inside the container using `docker exec`:
### Basic Commands
```bash
# Check status
docker exec basic-memory-server basic-memory status
# Sync files
docker exec basic-memory-server basic-memory sync
# Show help
docker exec basic-memory-server basic-memory --help
```
### Managing Projects with Volume Mounts
When using Docker volumes, you'll need to configure projects to point to your mounted directories:
1. **Check current configuration:**
```bash
docker exec basic-memory-server cat /app/.basic-memory/config.json
```
2. **Add a project for your mounted volume:**
```bash
# If you mounted /path/to/your/vault to /app/data
docker exec basic-memory-server basic-memory project create my-vault /app/data
# Set it as default
docker exec basic-memory-server basic-memory project set-default my-vault
```
3. **Sync the new project:**
```bash
docker exec basic-memory-server basic-memory sync
```
### Example: Setting up an Obsidian Vault
If you mounted your Obsidian vault like this in docker-compose.yml:
```yaml
volumes:
- /Users/yourname/Documents/ObsidianVault:/app/data:rw
```
Then configure it:
```bash
# Create project pointing to mounted vault
docker exec basic-memory-server basic-memory project create obsidian /app/data
# Set as default
docker exec basic-memory-server basic-memory project set-default obsidian
# Sync to index all files
docker exec basic-memory-server basic-memory sync
```
### Environment Variables
Configure Basic Memory using environment variables:
```yaml
environment:
# Default project
- BASIC_MEMORY_DEFAULT_PROJECT=main
# Enable real-time sync
- BASIC_MEMORY_SYNC_CHANGES=true
# Logging level
- BASIC_MEMORY_LOG_LEVEL=INFO
# Sync delay in milliseconds
- BASIC_MEMORY_SYNC_DELAY=1000
```
## File Permissions
### Linux/macOS
The Docker container now runs as a non-root user to avoid file ownership issues. By default, the container uses UID/GID 1000, but you can customize this to match your user:
```bash
# Build with custom UID/GID to match your user
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t basic-memory .
# Or use docker-compose with build args
```
**Example docker-compose.yml with custom user:**
```yaml
version: '3.8'
services:
basic-memory:
build:
context: .
dockerfile: Dockerfile
args:
UID: 1000 # Replace with your UID
GID: 1000 # Replace with your GID
container_name: basic-memory-server
ports:
- "8000:8000"
volumes:
- /path/to/your/obsidian-vault:/app/data:rw
- basic-memory-config:/app/.basic-memory:rw
environment:
- BASIC_MEMORY_DEFAULT_PROJECT=main
restart: unless-stopped
```
**Using pre-built images:**
If using the pre-built image from GitHub Container Registry, files will be created with UID/GID 1000. You can either:
1. Change your local directory ownership to match:
```bash
sudo chown -R 1000:1000 /path/to/your/obsidian-vault
```
2. Or build your own image with custom UID/GID as shown above.
### Windows
When using Docker Desktop on Windows, ensure the directories are shared:
1. Open Docker Desktop
2. Go to Settings → Resources → File Sharing
3. Add your knowledge directory path
4. Apply & Restart
## Troubleshooting
### Common Issues
1. **File Watching Not Working:**
- Ensure volume mounts are read-write (`:rw`)
- Check directory permissions
- On Linux, may need to increase inotify limits:
```bash
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```
2. **Configuration Not Persisting:**
- Use named volumes for `/app/.basic-memory`
- Check volume mount permissions
3. **Network Connectivity:**
- For HTTP transport, ensure port 8000 is exposed
- Check firewall settings
### Debug Mode
Run with debug logging:
```yaml
environment:
- BASIC_MEMORY_LOG_LEVEL=DEBUG
```
View logs:
```bash
docker-compose logs -f basic-memory
```
## Security Considerations
1. **Docker Security:**
The container runs as a non-root user (UID/GID 1000 by default) for improved security. You can customize the user ID using build arguments to match your local user.
2. **Volume Permissions:**
Ensure mounted directories have appropriate permissions and don't expose sensitive data. With the non-root container, files will be created with the specified user ownership.
3. **Network Security:**
If using HTTP transport, consider using reverse proxy with SSL/TLS and authentication if the endpoint is available on
a network.
4. **IMPORTANT:** The HTTP endpoints have no authorization. They should not be exposed on a public network.
## Integration Examples
### Claude Desktop with Docker
The recommended way to connect Claude Desktop to the containerized Basic Memory is using `mcp-proxy`, which converts the HTTP transport to STDIO that Claude Desktop expects:
1. **Start the Docker container:**
```bash
docker-compose up -d
```
2. **Configure Claude Desktop** to use mcp-proxy:
```json
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": [
"mcp-proxy",
"http://localhost:8000/mcp"
]
}
}
}
```
## Support
For Docker-specific issues:
1. Check the [troubleshooting section](#troubleshooting) above
2. Review container logs: `docker-compose logs basic-memory`
3. Verify volume mounts: `docker inspect basic-memory-server`
4. Test file permissions: `docker exec basic-memory-server ls -la /app`
For general Basic Memory support, see the main [README](../README.md)
and [documentation](https://memory.basicmachines.co/).
## GitHub Container Registry Images
### Available Images
Pre-built Docker images are available on GitHub Container Registry at [`ghcr.io/basicmachines-co/basic-memory`](https://github.com/basicmachines-co/basic-memory/pkgs/container/basic-memory).
**Supported architectures:**
- `linux/amd64` (Intel/AMD x64)
- `linux/arm64` (ARM64, including Apple Silicon)
**Available tags:**
- `latest` - Latest stable release
- `v0.13.8`, `v0.13.7`, etc. - Specific version tags
- `v0.13`, `v0.12`, etc. - Major.minor tags
### Automated Builds
Docker images are automatically built and published when new releases are tagged:
1. **Release Process:** When a git tag matching `v*` (e.g., `v0.13.8`) is pushed, the CI workflow automatically:
- Builds multi-platform Docker images
- Pushes to GitHub Container Registry with appropriate tags
- Uses native GitHub integration for seamless publishing
2. **CI/CD Pipeline:** The Docker workflow includes:
- Multi-platform builds (AMD64 and ARM64)
- Layer caching for faster builds
- Automatic tagging with semantic versioning
- Security scanning and optimization
### Setup Requirements (For Maintainers)
GitHub Container Registry integration is automatic for this repository:
1. **No external setup required** - GHCR is natively integrated with GitHub
2. **Automatic permissions** - Uses `GITHUB_TOKEN` with `packages: write` permission
3. **Public by default** - Images are automatically public for public repositories
The Docker CI workflow (`.github/workflows/docker.yml`) handles everything automatically when version tags are pushed.
-64
View File
@@ -1,64 +0,0 @@
# Basic Memory Engineering Style
Style is how we make code easier to verify. Prefer explicit, typed, local-first code that
preserves the file system as the source of truth while keeping the database, API, and MCP
surfaces in sync.
## Design Center
- Basic Memory is local-first. Markdown files are the durable source; SQLite/Postgres indexes
are derived state that should be rebuilt or reconciled from files when needed.
- Keep the existing boundary order: CLI/MCP/API entrypoints compose dependencies, services own
business behavior, repositories own database access, and file services own filesystem writes.
- MCP tools should remain atomic and composable. They should call API routers through typed MCP
clients, not reach around into services.
- Prefer small, explicit abstractions that match a real domain boundary. Avoid object
hierarchies when a function, dataclass, type alias, or protocol describes the concept better.
## Types And Data
- Use full type annotations and Python 3.12 syntax. Introduce `type` aliases for repeated
structured shapes, callback signatures, or domain concepts that would otherwise become
anonymous `dict[str, Any]` values.
- Use dataclasses for internal values, operation inputs, and service results. Prefer
`frozen=True` when the value should not change and `slots=True` when identity/dynamic
attributes are not needed.
- Use Pydantic v2 at boundaries that validate, serialize, or deserialize data: API payloads,
CLI/MCP schemas, configuration, and persistence-adjacent schemas.
- Use narrow `Protocol`s when a caller needs a capability rather than a concrete repository or
service. Keep protocols small enough that fake implementations in tests are obvious.
- Avoid speculative `getattr`, broad casts, or `Any` as a way to paper over uncertainty. Read
the model or schema definition and make the type relationship explicit.
## Control Flow And Resources
- Fail fast when an invariant is broken. Do not swallow exceptions, add warning-only error
handling, or introduce fallback behavior unless the user explicitly agrees to that behavior.
- Keep control flow simple and close to the domain decision. Push `if` statements up into the
function that owns orchestration; keep leaf helpers focused on computation or one side effect.
- Make async/resource boundaries visible with context managers and explicit lifecycles. Do not
start background work without a clear owner, cancellation story, and verification path.
- Keep file mutations centralized through the existing file utilities/services so checksum,
atomic write, and index synchronization behavior stays coherent.
## Testing And Verification
- Use evidence-first testing, not mechanical TDD. For bugs and risky behavior, add or update a
regression test that would catch the failure. For small documentation-only edits, use the
relevant doc/repo hygiene checks.
- Prefer tests that exercise real code paths. Use mocks, doubles, or `monkeypatch` only when
the external boundary would be slow, nondeterministic, or impossible to trigger directly.
- Keep coverage at 100% for new code. Use `# pragma: no cover` only for code that would require
disproportionate mocking and is covered through an integration or runtime path.
- Start with targeted commands, then widen as risk grows: focused pytest, `just fast-check`,
`just doctor`, package checks for agent packaging changes, and full SQLite/Postgres gates
when behavior crosses shared boundaries.
## Comments And Names
- Name values after the domain concept they carry: project, entity, permalink, tenant, route,
checksum, observation, relation, batch, or index state.
- Comments should say why a branch, invariant, retry, lifecycle, or compatibility constraint
exists. Section headers are useful when a function or file has clear phases.
- Avoid comments that restate the code. If a comment cannot explain a decision, simplify the
code or improve the name instead.
+260
View File
@@ -0,0 +1,260 @@
---
title: Getting Started with Basic Memory
type: note
permalink: docs/getting-started
---
# Getting Started with Basic Memory
This guide will help you install Basic Memory, configure it with Claude Desktop, and create your first knowledge notes
through conversations.
Basic Memory uses the [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) to connect with LLMs. It can be used with any service that supports the MCP, but Claude Desktop works especially well.
## Installation
### 1. Install Basic Memory
```bash
# Install with uv (recommended)
uv install basic-memory
# Or with pip
pip install basic-memory
```
> **Important**: You need to install Basic Memory using one of the commands above to use the command line tools.
### 2. Configure Claude Desktop
Claude Desktop often has trouble finding executables in your user path. Follow these steps for a reliable setup:
#### Step 1: Find the absolute path to uvx
Open Terminal and run:
```bash
which uvx
```
This will show you the full path (e.g., `/Users/yourusername/.cargo/bin/uvx`).
#### Step 2: Edit Claude Desktop Configuration
Edit the configuration file located at `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"basic-memory": {
"command": "/absolute/path/to/uvx",
"args": [
"basic-memory",
"mcp"
]
}
}
}
```
Replace `/absolute/path/to/uvx` with the actual path you found in Step 1.
> **Note**: Using absolute paths is necessary because Claude Desktop cannot access binaries in your user PATH.
#### Step 3: Restart Claude Desktop
Close and reopen Claude Desktop for the changes to take effect.
### 3. Start the Sync Service
Start the sync service to monitor your files for changes:
```bash
# One-time sync
basic-memory sync
# For continuous monitoring (recommended)
basic-memory sync --watch
```
The `--watch` flag enables automatic detection of file changes, keeping your knowledge base current.
### 4. Staying Updated
To update Basic Memory when new versions are released:
```bash
# Update with uv (recommended)
uv tool upgrade basic-memory
# Or with pip
pip install --upgrade basic-memory
```
> **Note**: After updating, you'll need to restart Claude Desktop and your sync process for changes to take effect.
## Troubleshooting Installation
### Common Issues
#### Claude Says "No Basic Memory Tools Available"
If Claude cannot find Basic Memory tools:
1. **Check absolute paths**: Ensure you're using complete absolute paths to uvx in the Claude Desktop configuration
2. **Verify installation**: Run `basic-memory --version` in Terminal to confirm Basic Memory is installed
3. **Restart applications**: Restart both Terminal and Claude Desktop after making configuration changes
4. **Check sync status**: Ensure `basic-memory sync --watch` is running
#### Permission Issues
If you encounter permission errors:
1. Check that Basic Memory has access to create files in your home directory
2. Ensure Claude Desktop has permission to execute the uvx command
## Creating Your First Knowledge Note
1. **Start the sync process** in a Terminal window:
```bash
basic-memory sync --watch
```
Keep this running in the background.
2. **Open Claude Desktop** and start a new conversation.
3. **Have a natural conversation** about any topic:
```
You: "Let's talk about coffee brewing methods I've been experimenting with."
Claude: "I'd be happy to discuss coffee brewing methods..."
You: "I've found that pour over gives more flavor clarity than French press..."
```
4. **Ask Claude to create a note**:
```
You: "Could you create a note summarizing what we've discussed about coffee brewing?"
```
5. **Confirm note creation**:
Claude will confirm when the note has been created and where it's stored.
6. **View the created file** in your `~/basic-memory` directory using any text editor or Obsidian.
The file structure will look similar to:
```markdown
---
title: Coffee Brewing Methods
permalink: coffee-brewing-methods
---
# Coffee Brewing Methods
## Observations
- [method] Pour over provides more clarity...
- [technique] Water temperature at 205°F...
## Relations
- relates_to [[Other Coffee Topics]]
```
## Using Special Prompts
Basic Memory includes special prompts that help you start conversations with context from your knowledge base:
### Continue Conversation
To resume a previous topic:
```
You: "Let's continue our conversation about coffee brewing."
```
This prompt triggers Claude to:
1. Search your knowledge base for relevant content about coffee brewing
2. Build context from these documents
3. Resume the conversation with full awareness of previous discussions
### Recent Activity
To see what you've been working on:
```
You: "What have we been discussing recently?"
```
This prompt causes Claude to:
1. Retrieve documents modified in the recent past
2. Summarize the topics and main points
3. Offer to continue any of those discussions
### Search
To find specific information:
```
You: "Find information about pour over coffee methods."
```
Claude will:
1. Search your knowledge base for relevant documents
2. Summarize the key findings
3. Offer to explore specific documents in more detail
See [[User Guide#Using Special Prompts]] for further information.
## Using Your Knowledge Base
### Referencing Knowledge
In future conversations, reference your existing knowledge:
```
You: "What water temperature did we decide was optimal for coffee brewing?"
```
Or directly reference notes using memory:// URLs:
```
You: "Take a look at memory://coffee-brewing-methods and let's discuss how to improve my technique."
```
### Building On Previous Knowledge
Basic Memory enables continuous knowledge building:
1. **Reference previous discussions** in new conversations
2. **Add to existing notes** through conversations
3. **Create connections** between related topics
4. **Follow relationships** to build comprehensive context
## Importing Existing Conversations
Import your existing AI conversations:
```bash
# From Claude
basic-memory import claude conversations
# From ChatGPT
basic-memory import chatgpt
```
After importing, run `basic-memory sync` to index everything.
## Quick Tips
- Keep `basic-memory sync --watch` running in a terminal window
- Use special prompts (Continue Conversation, Recent Activity, Search) to start contextual discussions
- Build connections between notes for a richer knowledge graph
- Use direct memory:// URLs when you need precise context
- Use git to version control your knowledge base
- Review and edit AI-generated notes for accuracy
## Next Steps
After getting started, explore these areas:
1. **Read the [[User Guide]]** for comprehensive usage instructions
2. **Understand the [[Knowledge Format]]** to learn how knowledge is structured
3. **Set up [[Obsidian Integration]]** for visual knowledge navigation
4. **Learn about [[Canvas]]** visualizations for mapping concepts
5. **Review the [[CLI Reference]]** for command line tools
+195
View File
@@ -0,0 +1,195 @@
---
title: Knowledge Format
type: note
permalink: docs/knowledge-format
tags:
- architecture
- patterns
- knowledge
- design
---
# Knowledge Format
Basic Memory uses standard Markdown with simple semantic patterns to create a knowledge graph. This document details the file structure and patterns used to organize knowledge.
## File-First Architecture
All knowledge in Basic Memory is stored in plain text Markdown files:
- Files are the source of truth for all knowledge
- Changes to files automatically update the knowledge graph
- You maintain complete ownership and control
- Files work with git and other version control systems
- Knowledge persists independently of any AI conversation
## Core Document Structure
Every document uses this basic structure:
```markdown
---
title: Document Title
type: note
tags: [tag1, tag2]
permalink: custom-path
---
# Document Title
Regular markdown content...
## Observations
- [category] Content with #tags (optional context)
## Relations
- relation_type [[Other Document]] (optional context)
```
### Frontmatter
The YAML frontmatter at the top of each file defines essential metadata:
```yaml
---
title: Document Title # Used for linking and references
type: note # Document type
tags: [tag1, tag2] # For organization and searching
permalink: custom-link # Optional custom URL path
---
```
The title is particularly important as it's used to create links between documents.
### Observations
Observations are facts or statements about a topic:
```markdown
## Observations
- [tech] Uses SQLite for storage #database
- [design] Follows local-first architecture #architecture
- [decision] Selected bcrypt for passwords #security (Based on audit)
```
Each observation contains:
- **Category** in [brackets] - classifies the information type
- **Content text** - the main information
- Optional **#tags** - additional categorization
- Optional **(context)** - supporting details
Common categories include:
- `[tech]`: Technical details
- `[design]`: Architecture decisions
- `[feature]`: User capabilities
- `[decision]`: Choices that were made
- `[principle]`: Fundamental concepts
- `[method]`: Approaches or techniques
- `[preference]`: Personal opinions
### Relations
Relations connect documents to form the knowledge graph:
```markdown
## Relations
- implements [[Search Design]]
- depends_on [[Database Schema]]
- relates_to [[User Interface]]
```
You can also create inline references:
```markdown
This builds on [[Core Design]] and uses [[Utility Functions]].
```
Common relation types include:
- `implements`: Implementation of a specification
- `depends_on`: Required dependency
- `relates_to`: General connection
- `inspired_by`: Source of ideas
- `extends`: Enhancement
- `part_of`: Component relationship
- `contains`: Hierarchical relationship
- `pairs_with`: Complementary relationship
## Knowledge Graph
Basic Memory automatically builds a knowledge graph from your document connections:
- Each document becomes a node in the graph
- Relations create edges between nodes
- Relation types add semantic meaning to connections
- Forward references can link to documents that don't exist yet
This graph enables rich context building and navigation across your knowledge base.
## Permalinks and memory:// URLs
Every document in Basic Memory has a unique permalink that serves as its stable identifier:
### How Permalinks Work
- **Automatically assigned**: The system generates a permalink for each document
- **Based on title**: By default, derived from the document title
- **Always unique**: If conflicts exist, the system adds a suffix to ensure uniqueness
- **Stable reference**: Remains the same even if the file moves in the directory structure
- **Used in memory:// URLs**: Forms the basis of the memory:// addressing scheme
You can specify a custom permalink in the frontmatter:
```yaml
---
title: Authentication Approaches
permalink: auth-approaches-2024
---
```
If not specified, one will be generated automatically from the title.
### Using memory:// URLs
The memory:// URL scheme provides a reliable way to reference knowledge:
```
memory://auth-approaches-2024 # Direct access by permalink
memory://Authentication Approaches # Access by title (automatically resolves)
memory://project/auth-approaches # Access by path
```
Memory URLs support pattern matching for more powerful queries:
```
memory://auth* # All documents with permalinks starting with "auth"
memory://*/approaches # All documents with permalinks ending with "approaches"
memory://project/*/requirements # All requirements documents in the project folder
memory://docs/search/implements/* # Follow all implements relations from search docs
```
This addressing scheme ensures content remains accessible even as your knowledge base evolves and files are reorganized.
## File Organization
Organize files in any structure that suits your needs:
```
docs/
architecture/
design.md
patterns.md
features/
search.md
auth.md
```
You can:
- Group by topic in folders
- Use a flat structure with descriptive filenames
- Tag files for easier discovery
- Add custom metadata in frontmatter
The system will build the semantic knowledge graph regardless of how you organize your files.
## Relations
- implemented_by [[User Guide]] (How to work with this format)
- relates_to [[Getting Started with Basic Memory]] (Setup instructions)
- explained_in [[Introduction to Basic Memory]] (Overview of the system)
-512
View File
@@ -1,512 +0,0 @@
# Note Format Reference
Every document in Basic Memory is a plain Markdown file. Files are the source of truth — changes to files automatically update the knowledge graph in the database. You maintain complete ownership, files work with git, and knowledge persists independently of any AI conversation.
## Document Structure
A note has three parts: YAML frontmatter, content (observations), and relations.
```markdown
---
title: Coffee Brewing Methods
type: note
tags: [coffee, brewing]
permalink: coffee-brewing-methods
---
# Coffee Brewing Methods
## Observations
- [method] Pour over provides more flavor clarity than French press
- [technique] Water temperature at 205°F extracts optimal compounds #brewing
- [preference] Ethiopian beans work well with lighter roasts (personal experience)
## Relations
- relates_to [[Coffee Bean Origins]]
- requires [[Proper Grinding Technique]]
- contrasts_with [[Tea Brewing Methods]]
```
The `## Observations` and `## Relations` headings are conventional but not required — the parser detects observations and relations by their syntax patterns anywhere in the document.
## Frontmatter
YAML metadata between `---` fences at the top of the file.
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `title` | No | filename stem | Used for linking and references. Auto-set from filename if missing. |
| `type` | No | `note` | Entity type. Used for schema resolution and filtering. |
| `tags` | No | `[]` | List or comma-separated string. Used for organization and search. |
| `permalink` | No | generated from title | Stable identifier. Persists even if the file moves. |
| `schema` | No | none | Schema attachment — dict (inline), string (reference), or omitted (implicit). |
Custom fields are allowed. Any key not in the standard set is stored as `entity_metadata` and indexed for search and filtering.
```yaml
---
title: Paul Graham
type: Person
tags: [startups, essays, lisp]
permalink: paul-graham
status: active
source: wikipedia
---
```
Here `status` and `source` are custom fields stored in `entity_metadata`.
### Frontmatter Value Handling
YAML automatically converts some values to native types. Basic Memory normalizes them:
- Date strings (`2025-10-24`) → kept as ISO format strings
- Numbers (`1.0`) → converted to strings
- Booleans (`true`) → converted to strings (`"True"`)
- Lists and dicts → preserved, items normalized recursively
This prevents errors when downstream code expects string values.
## Observations
An observation is a categorized fact about the entity. Written as a Markdown list item.
**Syntax:**
```
- [category] content text #tag1 #tag2 (context)
```
| Part | Required | Description |
|------|----------|-------------|
| `[category]` | Yes | Classification in square brackets. Any text except `[]()` chars. |
| content | Yes | The fact or statement. |
| `#tags` | No | Inline tags. Space-separated, each starting with `#`. |
| `(context)` | No | Parenthesized text at end of line. Supporting details or source. |
### Examples
```markdown
- [tech] Uses SQLite for storage #database
- [design] Follows local-first architecture #architecture
- [decision] Selected bcrypt for passwords #security (based on OWASP audit)
- [name] Paul Graham
- [expertise] Startups
- [expertise] Lisp
- [expertise] Essay writing
```
Array-like fields use repeated categories — multiple `[expertise]` observations above.
### What Is Not an Observation
The parser excludes these list item patterns:
| Pattern | Example | Reason |
|---------|---------|--------|
| Checkboxes | `- [ ] Todo item`, `- [x] Done`, `- [-] Cancelled` | Task list syntax |
| Markdown links | `- [text](url)` | URL link syntax |
| Bare wiki links | `- [[Target]]` | Treated as a `links_to` relation instead |
A list item with `#tags` but no `[category]` is still parsed — the tags are extracted and the category defaults to `Note`.
## Relations
Relations connect documents to form the knowledge graph. There are two kinds.
### Explicit Relations
Written as list items with a relation type and a `[[wiki link]]` target. Unquoted
relation types are single tokens. Quote relation types that contain spaces.
**Syntax:**
```
- relation_type [[Target Entity]] (context)
- "multi word relation type" [[Target Entity]] (context)
- 'multi word relation type' [[Target Entity]] (context)
```
| Part | Required | Description |
|------|----------|-------------|
| `relation_type` | Yes | Single unquoted token before `[[`, or quoted text for multi-word labels. |
| `[[Target]]` | Yes | Wiki link to the target entity. Matched by title or permalink. |
| `(context)` | No | Parenthesized text after `]]`. Supporting details. |
### Examples
Explicit relations:
```markdown
- implements [[Search Design]]
- depends_on [[Database Schema]]
- works_at [[Y Combinator]] (co-founder)
- "based on" [[Customer Interview]]
- 'in response to' [[Incident Review]]
```
Bare wiki links and prose list items create implicit `links_to` relations:
```markdown
- [[Some Entity]]
- some other thing [[Some Entity]]
```
Both examples above create `links_to [[Some Entity]]`. Use quotes when the words before
`[[` are meant to be a multi-word relation type.
Common relation types:
- `implements`, `depends_on`, `relates_to`, `inspired_by`
- `extends`, `part_of`, `contains`, `pairs_with`
- `works_at`, `authored`, `collaborated_with`
Any single-token text or quoted text works as a relation type. These are conventions,
not a fixed set.
### Inline References
Wiki links appearing in regular prose create implicit `links_to` relations. This includes
list items that do not match the explicit relation grammar above.
```markdown
This builds on [[Core Design]] and uses [[Utility Functions]].
- We should revisit [[Search Design]] after the API changes.
```
This creates three relations: `links_to [[Core Design]]`, `links_to [[Utility Functions]]`,
and `links_to [[Search Design]]`.
### Forward References
Relations can link to entities that don't exist yet. Basic Memory resolves them when the target is created.
## Permalinks and memory:// URLs
Every document has a unique **permalink** — a stable identifier derived from its title. You can set one explicitly in frontmatter, or let the system generate it.
```yaml
permalink: auth-approaches-2024
```
Permalinks form the basis of `memory://` URLs:
```
memory://auth-approaches-2024 # By permalink
memory://Authentication Approaches # By title (auto-resolves)
memory://project/auth-approaches # By path
```
Pattern matching is supported:
```
memory://auth* # Starts with "auth"
memory://*/approaches # Ends with "approaches"
memory://project/*/requirements # Nested wildcard
```
## Schemas
Schemas declare the expected structure of a note — which observation categories and relation types a well-formed note should have. They use Picoschema, a compact notation from Google's Dotprompt that fits naturally in YAML frontmatter.
### Picoschema Syntax
```yaml
schema:
name: string, full name # required field with description
email?: string, contact email # ? = optional
role?: string, job title
works_at?: Organization, employer # capitalized type = entity reference
tags?(array): string, categories # array of type
status?(enum): [active, inactive] # enum with allowed values
metadata?(object): # nested object
updated_at?: string
source?: string
```
| Notation | Meaning | Example |
|----------|---------|---------|
| `field: type` | Required field | `name: string` |
| `field?: type` | Optional field | `role?: string` |
| `field(array): type` | Array of values | `expertise(array): string` |
| `field?(enum): [vals]` | Enum with allowed values | `status?(enum): [active, inactive]` |
| `field?(object):` | Nested object with sub-fields | `metadata?(object):` |
| `, description` | Description after comma | `name: string, full name` |
| `EntityName` | Capitalized type = entity reference | `works_at?: Organization` |
**Scalar types:** `string`, `integer`, `number`, `boolean`, `any`
Any type not in that set whose first letter is uppercase is treated as an entity reference (a relation target).
### Schema-to-Note Mapping
Schemas validate against existing observation/relation syntax. Note authors don't learn new syntax.
| Schema Declaration | Maps To | Example in Note |
|--------------------|---------|-----------------|
| `field: string` | Observation `[field] value` | `- [name] Paul Graham` |
| `field?(array): string` | Multiple `[field]` observations | `- [expertise] Lisp` (repeated) |
| `field?: EntityType` | Relation `field [[Target]]` | `- works_at [[Y Combinator]]` |
| `field?(array): EntityType` | Multiple `field` relations | `- authored [[Book]]` (repeated) |
| `tags` | Frontmatter `tags` array | `tags: [startups, essays]` |
| `field?(enum): [vals]` | Observation `[field] value` where value is in the set | `- [status] active` |
Observations and relations not covered by the schema are valid — schemas describe a subset, not a straitjacket.
### Schema Attachment
Three ways to attach a schema to a note, resolved in priority order:
**1. Inline schema**`schema` is a dict in frontmatter:
```yaml
---
title: Team Standup 2024-01-15
type: meeting
schema:
attendees(array): string, who was there
decisions(array): string, what was decided
action_items(array): string, follow-ups
blockers?(array): string, anything stuck
---
```
Good for one-off structured notes or prototyping a schema before extracting it.
**2. Explicit reference**`schema` is a string naming a schema note:
```yaml
---
title: Basic Memory
schema: SoftwareProject
---
```
or by permalink:
```yaml
---
title: LLM Memory Patterns
schema: schema/research-project
---
```
Use when the note's `type` differs from the schema it should validate against, or when multiple schema variants exist.
**3. Implicit by type** — no `schema` field, resolved by matching `type`:
```yaml
---
title: Paul Graham
type: Person
---
```
The system looks up a schema note where `entity: Person`. If found, it applies. If not, no validation occurs.
**4. No schema** — perfectly fine. Most notes don't need one.
### Schema Notes
A schema is itself a Basic Memory note with `type: schema`. It lives anywhere (though `schema/` is the conventional directory).
```yaml
# schema/Person.md
---
title: Person
type: schema
entity: Person
version: 1
schema:
name: string, full name
role?: string, job title or position
works_at?: Organization, employer
expertise?(array): string, areas of knowledge
email?: string, contact email
settings:
validation: warn
---
# Person
A human individual in the knowledge graph.
```
| Field | Required | Description |
|-------|----------|-------------|
| `type` | Yes | Must be `schema` |
| `entity` | Yes | The entity type this schema describes (e.g., `Person`) |
| `version` | No | Schema version number (default: `1`) |
| `schema` | Yes | Picoschema dict defining the fields |
| `settings.validation` | No | Validation mode (default: `warn`) |
Schema notes are regular notes — they show up in search, can have observations and relations, and participate in the knowledge graph.
### Validation Modes
| Mode | Behavior |
|------|----------|
| `warn` | Warnings in output, doesn't block (default) |
| `strict` | Errors that block sync, for CI/CD enforcement |
| `off` | No validation |
### Validation Output
```
$ bm schema validate people/ada-lovelace.md
⚠ Person schema validation:
- Missing required field: name (expected [name] observation)
- Missing optional field: role
- Missing optional field: works_at (no relation found)
Unmatched observations: [fact] ×2, [born] ×1
Unmatched relations: collaborated_with
```
"Unmatched" items are informational — observations and relations the schema doesn't cover.
### Schema Inference
Generate schemas from existing notes by analyzing observation and relation frequency:
```
$ bm schema infer Person
Analyzing 30 notes with type: Person...
Observations found:
[name] 30/30 100% → name: string
[role] 27/30 90% → role?: string
[expertise] 18/30 60% → expertise?(array): string
[email] 8/30 27% → email?: string
Relations found:
works_at 22/30 73% → works_at?: Organization
Suggested schema:
name: string, full name
role?: string, job title
expertise?(array): string, areas of knowledge
email?: string, contact email
works_at?: Organization, employer
Save to schema/Person.md? [y/n]
```
Frequency thresholds:
- **100% present** → required field
- **25%+ present** → optional field
- **Below 25%** → excluded from suggestion
### Schema Drift Detection
Track how usage patterns shift over time:
```
$ bm schema diff Person
Schema drift detected:
+ expertise: now in 81% of notes (was 12%)
- department: dropped to 3% of notes
~ works_at: cardinality changed (one → many)
Update schema? [y/n/review]
```
## Complete Examples
### Simple Note (No Schema)
```markdown
---
title: Project Ideas
type: note
tags: [ideas, brainstorm]
---
# Project Ideas
## Observations
- [idea] Build a CLI tool for markdown linting #tooling
- [idea] Create a recipe knowledge base #cooking
- [priority] Focus on developer tools first (Q1 goal)
## Relations
- inspired_by [[Developer Workflow Research]]
- part_of [[Q1 Planning]]
```
### Schema-Validated Note
Schema at `schema/Person.md`:
```yaml
---
title: Person
type: schema
entity: Person
version: 1
schema:
name: string, full name
role?: string, job title or position
works_at?: Organization, employer
expertise?(array): string, areas of knowledge
email?: string, contact email
settings:
validation: warn
---
# Person
A human individual in the knowledge graph.
```
Note at `people/paul-graham.md`:
```markdown
---
title: Paul Graham
type: Person
tags: [startups, essays, lisp]
---
# Paul Graham
## Observations
- [name] Paul Graham
- [role] Essayist and investor
- [expertise] Startups
- [expertise] Lisp
- [expertise] Essay writing
- [fact] Created Viaweb, the first web app
## Relations
- works_at [[Y Combinator]]
- authored [[Hackers and Painters]]
```
The `[fact]` observation and `authored` relation are not in the schema — they're valid, just unmatched. The schema only checks that `[name]` exists (required) and looks for optional fields like `[role]`, `[expertise]`, and `works_at`.
### Inline Schema Note
```markdown
---
title: Team Standup 2024-01-15
type: meeting
schema:
attendees(array): string, who was there
decisions(array): string, what was decided
action_items(array): string, follow-ups
blockers?(array): string, anything stuck
---
# Team Standup 2024-01-15
## Observations
- [attendees] Paul
- [attendees] Sarah
- [decisions] Ship v2 by Friday
- [action_items] Paul to review PR #42
- [blockers] Waiting on API credentials
```
+146
View File
@@ -0,0 +1,146 @@
---
title: Obsidian Integration
type: note
permalink: docs/obsidian-integration
---
# Obsidian Integration
Basic Memory integrates seamlessly with [Obsidian](https://obsidian.md), providing powerful visualization and navigation capabilities for your knowledge graph.
## Setup
### Creating an Obsidian Vault
1. Download and install [Obsidian](https://obsidian.md)
2. Create a new vault
3. Point it to your Basic Memory directory (~/basic-memory by default)
4. Enable core plugins like Graph View, Backlinks, and Tags
## Visualization Features
### Graph View
Obsidian's Graph View provides a visual representation of your knowledge network:
- Each document appears as a node
- Relations appear as connections between nodes
- Colors can be customized to distinguish types
- Filters let you focus on specific aspects
- Local graphs show connections for individual documents
### Backlinks
Obsidian automatically tracks references between documents:
- View all documents that reference the current one
- See the exact context of each reference
- Navigate easily through connections
- Track how concepts relate to each other
### Tag Explorer
Use tags to organize and filter content:
- View all tags in your knowledge base
- See how many documents use each tag
- Filter documents by tag combinations
- Create hierarchical tag structures
## Knowledge Elements
Basic Memory's knowledge format works natively with Obsidian:
### Wiki Links
```markdown
## Relations
- implements [[Search Design]]
- depends_on [[Database Schema]]
```
These display as clickable links in Obsidian and appear in the graph view.
### Observations with Tags
```markdown
## Observations
- [tech] Using SQLite #database
- [design] Local-first #architecture
```
Tags become searchable and filterable in Obsidian's tag pane.
### Frontmatter
```yaml
---
title: Document Title
type: note
tags: [search, design]
---
```
Frontmatter provides metadata for Obsidian to use in search and filtering.
## Canvas Integration
Basic Memory can create [Obsidian Canvas](https://obsidian.md/canvas) files:
1. Ask Claude to create a visualization:
```
You: "Create a canvas showing the structure of our project components."
```
2. Claude generates a .canvas file in your knowledge base
3. Open the file in Obsidian to view and edit the visual representation
4. Canvas files maintain references to your documents
## Recommended Plugins
These Obsidian plugins work especially well with Basic Memory:
- **Dataview**: Query your knowledge base programmatically
- **Kanban**: Organize tasks from knowledge files
- **Calendar**: View and navigate temporal knowledge
- **Templates**: Create consistent knowledge structures
## Workflow Suggestions
### Daily Notes
```markdown
# 2024-01-21
## Progress
- Updated [[Search Design]]
- Fixed [[Bug Report 123]]
## Notes
- [idea] Better indexing #enhancement
- [todo] Update docs #documentation
## Links
- relates_to [[Current Sprint]]
- updates [[Project Status]]
```
### Project Tracking
```markdown
# Current Sprint
## Tasks
- [ ] Update [[Search]]
- [ ] Fix [[Auth Bug]]
## Tags
#sprint #planning #current
```
## Relations
- enhances [[Introduction to Basic Memory]] (Overview of system)
- relates_to [[Canvas]] (Visual knowledge mapping)
- complements [[User Guide]] (Using Basic Memory)
-147
View File
@@ -1,147 +0,0 @@
# Simplified Local/Cloud Routing
## Context
Basic Memory now uses explicit, project-aware routing without a global cloud-mode toggle.
Routing is determined by command-level flags and project mode, not by a global `cloud_mode` state.
This document is the canonical contract for local/cloud routing behavior in CLI, MCP, and API-adjacent clients.
## Goals
1. Remove global `cloud_mode` from runtime/routing semantics.
2. Keep MCP HTTP/SSE local-only; let stdio honor per-project routing.
3. Make CLI routing explicit and easy to reason about.
4. Support projects that exist in both local and cloud without ambiguity.
## Routing Contract
Routing is resolved in this order:
1. Injected client factory (for composition/integration contexts)
2. Explicit routing override (`--local` / `--cloud` or env vars below)
3. Project-scoped routing (`project.mode`) when a project is known
4. Default local routing
### Routing Environment Variables
- `BASIC_MEMORY_FORCE_LOCAL=true`: force local transport
- `BASIC_MEMORY_FORCE_CLOUD=true`: force cloud proxy transport
- `BASIC_MEMORY_EXPLICIT_ROUTING=true`: marks routing as explicitly chosen for this command
When explicit routing is active, project mode does not override the selected route.
## Config Semantics
- `project.mode` is the only config-based routing signal for project-scoped operations.
- Legacy `cloud_mode` values may be encountered during migration/loading but are not used for routing behavior.
- Normalization saves remove stale `cloud_mode` from `~/.basic-memory/config.json`.
### Example Config
```json
{
"projects": {
"main": {
"path": "/Users/me/basic-memory",
"mode": "local",
"local_sync_path": null,
"bisync_initialized": false,
"last_sync": null
},
"specs": {
"path": "specs",
"mode": "cloud",
"local_sync_path": "/Users/me/dev/specs",
"bisync_initialized": true,
"last_sync": "2026-02-06T17:36:38.544153"
}
},
"default_project": "main",
"cloud_api_key": "bmc_abc123...",
"cloud_host": "https://cloud.basicmemory.com"
}
```
## Cloud Commands Are Auth-Only
`bm cloud login`, `bm cloud logout`, and `bm cloud status` manage authentication state.
- `bm cloud login`
- performs OAuth device flow
- stores/refreshes token material
- may verify cloud health/subscription
- does not change routing defaults
- `bm cloud logout`
- removes stored OAuth session tokens
- does not change routing defaults
- `bm cloud status`
- reports auth state (API key, OAuth token validity)
- runs health checks only when credentials are available
## MCP Transport Routing
### Stdio (default)
`bm mcp --transport stdio` uses natural per-project routing.
- Local-mode projects route through the in-process ASGI transport.
- Cloud-mode projects route to the cloud proxy with Bearer auth (API key).
- No explicit routing env vars are injected by the CLI command.
- Externally-set env vars are honored (e.g. `BASIC_MEMORY_FORCE_CLOUD=true` for cloud deployments).
- Users who need all projects forced local can set `BASIC_MEMORY_FORCE_LOCAL=true` externally.
### HTTP and SSE Transports
`bm mcp --transport streamable-http` and `bm mcp --transport sse` always route locally.
These transports set explicit local routing (`BASIC_MEMORY_FORCE_LOCAL=true` and
`BASIC_MEMORY_EXPLICIT_ROUTING=true`) before starting the server. This prevents cloud
routing regardless of project mode, since HTTP/SSE serve as local API endpoints.
## Project List UX for Dual Presence
Projects may exist in both local and cloud. `bm project list` should display that clearly in one row per logical
project identity, with explicit source/target signals.
Recommended display contract:
1. Keep one row per normalized project name/permalink.
2. Show both local and cloud presence as separate columns/indicators.
3. Show an explicit `MCP (stdio)` target column that always resolves to `local`.
4. Keep CLI route semantics explicit:
- no flags: default local for non-project commands
- `--cloud`: force cloud
- `--local`: force local
## Project LS Targeting
`bm project ls` should clearly identify which project instance is being listed.
Targeting rules:
1. No routing flags: list local project files.
2. `--cloud`: list cloud project files.
3. `--local`: list local project files (explicit override).
4. Output should label the active target (`LOCAL` or `CLOUD`) in heading or status line.
## Runtime Mode
Runtime mode is no longer a cloud/local routing switch for local app flows.
- `resolve_runtime_mode(is_test_env)` resolves to:
- `TEST` when running in test environment
- `LOCAL` otherwise
- `RuntimeMode.CLOUD` may remain for compatibility with existing tests/call sites but is not selected by normal local
runtime resolution.
## Verification Checklist
1. Loading config with legacy `cloud_mode` succeeds.
2. Saving config strips legacy `cloud_mode`.
3. `--local/--cloud` always override per-project mode for that command.
4. No-project + no-flags commands route local by default.
5. `bm cloud login/logout` do not toggle routing behavior.
6. `bm mcp` stdio routes per-project mode; HTTP/SSE remain local-forced.
7. `bm project list` communicates dual local/cloud presence without ambiguity.
8. `bm project ls` output identifies route target explicitly.
+243
View File
@@ -0,0 +1,243 @@
---
title: Technical Information
type: note
permalink: docs/technical-information
---
# Technical Information
This document provides technical details about Basic Memory's implementation, licensing, and integration with the Model Context Protocol (MCP).
## Architecture
Basic Memory consists of:
1. **Core Knowledge Engine**: Parses and indexes Markdown files
2. **SQLite Database**: Provides fast querying and search
3. **MCP Server**: Implements the Model Context Protocol
4. **CLI Tools**: Command-line utilities for management
5. **Sync Service**: Monitors file changes and updates the database
The system follows a file-first architecture where all knowledge is represented in standard Markdown files and the database serves as a secondary index.
## Model Context Protocol (MCP)
Basic Memory implements the [Model Context Protocol](https://github.com/modelcontextprotocol/spec), an open standard for enabling AI models to access external tools:
- **Standardized Interface**: Common protocol for tool integration
- **Tool Registration**: Basic Memory registers as a tool provider
- **Asynchronous Communication**: Enables efficient interaction with AI models
- **Standardized Schema**: Structured data exchange format
Integration with Claude Desktop uses the MCP to grant Claude access to your knowledge base through a set of specialized tools that search, read, and write knowledge.
## Licensing
Basic Memory is licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html):
- **Free Software**: You can use, study, share, and modify the software
- **Copyleft**: Derivative works must be distributed under the same license
- **Network Use**: Network users must be able to receive the source code
- **Commercial Use**: Allowed, subject to license requirements
The AGPL license ensures Basic Memory remains open source while protecting against proprietary forks.
## Source Code
Basic Memory is developed as an open-source project:
- **GitHub Repository**: [https://github.com/basicmachines-co/basic-memory](https://github.com/basicmachines-co/basic-memory)
- **Issue Tracker**: Report bugs and request features on GitHub
- **Contributions**: Pull requests are welcome following the contributing guidelines
- **Documentation**: Source for this documentation is also available in the repository
## Data Storage and Privacy
Basic Memory is designed with privacy as a core principle:
- **Local-First**: All data remains on your local machine
- **No Cloud Dependency**: No remote servers or accounts required
- **Telemetry**: Optional and disabled by default
- **Standard Formats**: All data is stored in standard file formats you control
## Implementation Details
Knowledge in Basic Memory is organized as a semantic graph:
1. **Entities** - Distinct concepts represented by Markdown documents
2. **Observations** - Categorized facts and information about entities
3. **Relations** - Connections between entities that form the knowledge graph
This structure emerges from simple text patterns in standard Markdown:
```markdown
---
title: Coffee Brewing Methods
type: note
permalink: coffee/coffee-brewing-methods
tags:
- '#coffee'
- '#brewing'
- '#methods'
- '#demo'
---
# Coffee Brewing Methods
An exploration of different coffee brewing techniques, their characteristics, and how they affect flavor extraction.
## Overview
Coffee brewing is both an art and a science. Different brewing methods extract different compounds from coffee beans,
resulting in unique flavor profiles, body, and mouthfeel. The key variables in any brewing method are:
- Grind size
- Water temperature
- Brew time
- Coffee-to-water ratio
- Agitation/turbulence
## Observations
- [principle] Coffee extraction follows a predictable pattern: acids extract first, then sugars, then bitter compounds
#extraction
- [method] Pour over methods generally produce cleaner, brighter cups with more distinct flavor notes #clarity
## Relations
- requires [[Proper Grinding Technique]]
- affects [[Flavor Extraction]]
```
Becomes
```json
{
"entities": [
{
"permalink": "coffee/coffee-brewing-methods",
"title": "Coffee Brewing Methods",
"file_path": "Coffee Notes/Coffee Brewing Methods.md",
"entity_type": "note",
"entity_metadata": {
"title": "Coffee Brewing Methods",
"type": "note",
"permalink": "coffee/coffee-brewing-methods",
"tags": "['#coffee', '#brewing', '#methods', '#demo']"
},
"checksum": "bfa32a0f23fa124b53f0694c344d2788b0ce50bd090b55b6d738401d2a349e4c",
"content_type": "text/markdown",
"observations": [
{
"category": "principle",
"content": "Coffee extraction follows a predictable pattern: acids extract first, then sugars, then bitter compounds #extraction",
"tags": [
"extraction"
],
"permalink": "coffee/coffee-brewing-methods/observations/principle/coffee-extraction-follows-a-predictable-pattern-acids-extract-first-then-sugars-then-bitter-compounds-extraction"
},
{
"category": "method",
"content": "Pour over methods generally produce cleaner, brighter cups with more distinct flavor notes #clarity",
"tags": [
"clarity"
],
"permalink": "coffee/coffee-brewing-methods/observations/method/pour-over-methods-generally-produce-cleaner-brighter-cups-with-more-distinct-flavor-notes-clarity"
}
],
"relations": [
{
"from_id": "coffee/coffee-bean-origins",
"to_id": "coffee/coffee-brewing-methods",
"relation_type": "pairs_with",
"permalink": "coffee/coffee-bean-origins/pairs-with/coffee/coffee-brewing-methods",
"to_name": "Coffee Brewing Methods"
},
{
"from_id": "coffee/flavor-extraction",
"to_id": "coffee/coffee-brewing-methods",
"relation_type": "affected_by",
"permalink": "coffee/flavor-extraction/affected-by/coffee/coffee-brewing-methods",
"to_name": "Coffee Brewing Methods"
}
],
"created_at": "2025-03-06T14:01:23.445071",
"updated_at": "2025-03-06T13:34:48.563606"
}
]
}
```
Basic Memory understands how to build context via its semantic graph.
### Entity Model
Basic Memory's core data model consists of:
- **Entities**: Documents in your knowledge base
- **Observations**: Facts or statements about entities
- **Relations**: Connections between entities
- **Tags**: Additional categorization for entities and observations
The system parses Markdown files to extract this structured information while preserving the human-readable format.
### Files as Source of Truth
Plain Markdown files store all knowledge, making it accessible with any text editor and easy to version with git.
```mermaid
flowchart TD
User((User)) <--> |Conversation| Claude["Claude or other LLM"]
Claude <-->|API Calls| BMCP["Basic Memory MCP Server"]
subgraph "Local Storage"
KnowledgeFiles["Markdown Files - Source of Truth"]
KnowledgeIndex[(Knowledge Graph SQLite Index)]
end
BMCP <-->|"write_note() read_note()"| KnowledgeFiles
BMCP <-->|"search() build_context()"| KnowledgeIndex
KnowledgeFiles <-.->|Sync Process| KnowledgeIndex
KnowledgeFiles <-->|Direct Editing| Editors((Text Editors & Git))
User -.->|"Complete control, Privacy preserved"| KnowledgeFiles
class Claude primary
class BMCP secondary
class KnowledgeFiles tertiary
class KnowledgeIndex quaternary
class User,Editors user`;
```
### Sqlite Database
A local SQLite database maintains the knowledge graph topology for fast queries and semantic traversal without cloud dependencies. It contains:
- db tables for the knowledge graph schema
- a search index table enabling full text search across the knowledge base
### Sync Process
The sync process:
1. Detects changes to files in the knowledge directory
2. Parses modified files to extract structured data
3. Updates the SQLite database with changes
4. Resolves forward references when new entities are created
5. Updates the search index for fast querying
### Search Engine
The search functionality:
1. Uses a combination of full-text search and semantic matching
2. Indexes observations, relations, and content
3. Supports wildcards and pattern matching in memory:// URLs
4. Traverses the knowledge graph to follow relationships
5. Ranks results by relevance to the query
## Relations
- relates_to [[Welcome to Basic memory]] (Overview)
- relates_to [[CLI Reference]] (Command line tools)
- implements [[Knowledge Format]] (File structure and format)
+556
View File
@@ -0,0 +1,556 @@
---
title: User Guide
type: note
permalink: docs/user-guide
---
# User Guide
This guide explains how to effectively use Basic Memory in your daily workflow, from creating knowledge through
conversations to building a rich semantic network.
## Basic Memory Workflow
Using Basic Memory follows a natural cycle:
1. **Have conversations** with AI assistants like Claude
2. **Capture knowledge** in Markdown files
3. **Build connections** between pieces of knowledge
4. **Reference your knowledge** in future conversations
5. **Edit files directly** when needed
6. **Sync changes** automatically
## Creating Knowledge
### Through Conversations
To create knowledge during conversations with Claude:
```
You: We've covered several authentication approaches. Could you create a note summarizing what we've discussed?
Claude: I'll create a note summarizing our authentication discussion.
```
This creates a Markdown file in your `~/basic-memory` directory with semantic markup.
### Direct File Creation
You can create files directly:
1. Create a new Markdown file in your `~/basic-memory` directory
2. Add frontmatter with title, type, and optional tags
3. Structure content with observations and relations
4. Save the file
5. Run `basic-memory sync` if not in watch mode
## Using Special Prompts
Basic Memory includes several special prompts that help you leverage your knowledge base more effectively. In apps like
Claude Desktop, these prompts trigger specific tools to search and analyze your knowledge base.
### Continue Conversation
When you want to pick up where you left off on a topic:
```
You: Let's continue our conversation about authentication systems.
```
Behind the scenes:
- Claude searches your knowledge base for content about "authentication systems"
- It retrieves relevant documents and their relations
- It analyzes the context to understand where you left off
- It builds a comprehensive picture of what you've previously discussed
- It can then resume the conversation with all that context
This is particularly useful when:
- Starting a new session days or weeks after your last discussion
- Switching between multiple ongoing projects
- Building on previous work without repeating yourself
### Recent Activity
To get an overview of what you've been working on:
```
You: What have we been discussing recently?
```
Behind the scenes:
- Claude retrieves documents modified recently
- It analyzes patterns and themes
- It summarizes the key topics and changes
- It offers to continue working on any of those topics
This is useful for:
- Coming back after a break
- Getting a quick reminder of ongoing projects
- Deciding what to work on next
### Search
To find specific information in your knowledge base:
```
You: Find information about JWT authentication in my notes.
```
Behind the scenes:
- Claude performs a semantic search for "JWT authentication"
- It retrieves and ranks the most relevant documents
- It summarizes the key findings
- It offers to explore specific areas in more detail
This is useful for:
- Finding specific information quickly
- Exploring what you know about a topic
- Starting work on an existing topic
### Example
Choose "Continue Conversation"
![[prompt 1.png|500]]
Enter a topic
![[prompt2.png|500]]
Give instructions
![[prompt3.png|500]]
Claude Desktop lets you send a prompt to provide context. You can use this at the beginning of a chat to preload context
without needing to copy paste all the time. By using one of the supplied prompts, Basic Memory will search the knowledge
base and give the AI instructions for how to build context.
Choose "Continue Conversation":
![[prompt 1.png|500]]
Enter a topic:
![[prompt2.png|500]]
Give optional additional instructions:
![[prompt3.png|500]]
Claude can build context from the supplied topic. This works independently of Claude Project information. All the
context comes from your local knowledge base.
![[prompt4.png|500]]
## Searching Your Knowledge Base
Basic Memory provides multiple ways to search and explore your knowledge base:
### Natural Language Search
The simplest way to search is to ask Claude directly:
```
You: What do I know about authentication methods?
```
Claude will search your knowledge base semantically and return relevant information.
### Search Prompt
Use the dedicated search prompt for more focused searches:
```
You: Search for "JWT authentication"
```
This triggers a specialized search that returns precise results with document titles, relevant excerpts, and offers to
explore specific documents.
### Boolean Search
For more precise searches, use boolean operators to refine your queries:
```
You: Search for "authentication AND OAuth NOT basic"
```
Basic Memory supports standard boolean operators:
- **AND**: Find documents containing both terms
```
You: Search for "python AND flask"
```
This finds documents containing both "python" and "flask"
- **OR**: Find documents containing either term
```
You: Search for "python OR javascript"
```
This finds documents containing either "python" or "javascript"
- **NOT**: Exclude documents containing specific terms
```
You: Search for "python NOT django"
```
This finds documents containing "python" but excludes those containing "django"
- **Grouping with parentheses**: Control operator precedence
```
You: Search for "(python OR javascript) AND web"
```
This finds documents about web development that mention either Python or JavaScript
Boolean search is particularly useful for:
- Narrowing down results in large knowledge bases
- Finding specific combinations of concepts
- Excluding irrelevant content from search results
- Creating complex queries for precise information retrieval
### Memory URL Pattern Matching
For advanced searches, use memory:// URL patterns with wildcards:
```
You: Look at memory://auth* and summarize all authentication approaches.
```
Pattern matching supports:
- **Wildcards**: `memory://auth*` matches all permalinks starting with "auth"
- **Path patterns**: `memory://project/*/auth` matches auth documents in any project subfolder
- **Relation traversal**: `memory://auth-system/implements/*` finds all documents that implement the auth system
### Combining Search with Context Building
The most powerful searches build comprehensive context by following relationships:
```
You: Search for JWT authentication and then follow all implementation relations.
```
This builds a complete picture by:
1. Finding documents about JWT authentication
2. Following implementation relationships from those documents
3. Building a complete picture of how JWT is implemented across your system
### Search Best Practices
For effective searching:
1. **Be specific** with search terms and phrases
2. **Use boolean operators** to refine searches and find precise information
3. **Use technical terms** when searching for technical content
4. **Follow up** on search results by asking for more details about specific documents
5. **Combine approaches** by starting with search and then using memory:// URLs for precision
6. **Use relation traversal** to explore connected concepts after finding initial documents
## Referencing Knowledge
### Using memory:// URLs
Reference specific knowledge directly:
```
You: Please look at memory://authentication-approaches and suggest which approach would be best for our mobile app.
```
### Natural Language References
Reference knowledge conversationally:
```
You: What did we decide about authentication for the project?
```
### Advanced References
Follow connections across your knowledge graph:
```
You: Look at memory://project-architecture and check related documents to give me a complete picture.
```
## Working with Files
### File Location and Organization
By default, Basic Memory stores files in `~/basic-memory`:
- Browse this directory in your file explorer
- Organize files into subfolders
- Use git for version control
### File Format
Each knowledge file follows this structure:
```markdown
---
title: Authentication Approaches
type: note
tags: [security, architecture]
permalink: authentication-approaches
---
# Authentication Approaches
A comparison of authentication methods.
## Observations
- [approach] JWT provides stateless authentication #security
- [limitation] Session tokens require server-side storage #infrastructure
## Relations
- implements [[Security Requirements]]
- affects [[User Login Flow]]
```
### Editing Files
Modify files in any text editor:
1. Open the file in your preferred editor
2. Make changes to content, observations, or relations
3. Save the file
4. Basic Memory detects changes automatically when running in watch mode
## Building a Knowledge Graph
The value of Basic Memory comes from connections between pieces of knowledge.
### Creating Relations
When creating or editing notes, build connections:
```markdown
## Relations
- implements [[Security Requirements]]
- depends_on [[User Authentication]]
```
Relations can be:
- Hierarchical (part_of, contains)
- Directional (implements, depends_on)
- Associative (relates_to, similar_to)
- Temporal (precedes, follows)
Relations are also created via regular wiki-link style links within the body text.
### Forward References
Reference documents that don't exist yet:
```markdown
- will_impact [[Future Feature]]
```
These references resolve automatically when you create the referenced document.
## Conversation Continuity
Basic Memory maintains context across different conversations.
### Starting New Sessions with Context
When starting a new conversation with Claude, you can:
1. **Use special prompts** like "Continue conversation about..." or "What were we working on?"
2. **Reference specific documents** with memory:// URLs
3. **Ask about recent work** with "What have we been discussing recently?"
4. **Search for specific topics** with "Find information about..."
### Long-Term Projects
Maintain context for complex projects over time:
1. **Document key decisions** as you make them
2. **Create relationships** between project components
3. **Reference past decisions** when implementing features
4. **Update documentation** as the project evolves
### Tips for Effective Continuity
1. **Be specific about topics** when continuing a conversation
2. **Reference documents directly** with memory:// URLs for precision
3. **Create summary notes** after important discussions
4. **Update existing notes** rather than creating duplicates
5. **Build robust connections** between related topics
## Advanced Features
### Importing External Knowledge
Import existing conversations:
```bash
# From Claude
basic-memory import claude conversations
# From ChatGPT
basic-memory import chatgpt
```
After importing, run `basic-memory sync` to index everything.
### Obsidian Integration
Use with [Obsidian](https://obsidian.md):
1. Point Obsidian to your `~/basic-memory` directory
2. Use Obsidian's graph view to visualize your knowledge network
3. All changes sync back to Basic Memory
### Canvas Visualizations
Create visual knowledge maps:
```
You: Could you create a canvas visualization of our project components?
```
This generates an Obsidian canvas file showing the relationships between concepts.
### Advanced Memory URI Patterns
Use wildcards and patterns:
```
You: Review memory://project/*/requirements to summarize all project requirements.
```
## Command Line Interface
### Sync Commands
```bash
# One-time sync
basic-memory sync
# Watch for changes
basic-memory sync --watch
```
### Status and Information
```bash
# Check system status
basic-memory status
# View CLI help
basic-memory --help
```
### Import Commands
```bash
# Import from Claude
basic-memory import claude conversations
# Import from ChatGPT
basic-memory import chatgpt
```
## Multiple Projects
Basic Memory supports managing multiple separate knowledge bases through projects. This feature allows you to maintain
separate knowledge graphs for different purposes (e.g., personal notes, work projects, research topics).
Basic Memory keeps a list of projects in a config file: ` ~/.basic-memory/config.json`
### Managing Projects
```bash
# List all configured projects
basic-memory project list
# Add a new project
basic-memory project add work ~/work-basic-memory
# Set the default project
basic-memory project default work
# Remove a project (doesn't delete files)
basic-memory project remove personal
# Show current project
basic-memory project current
```
### Using Projects in Commands
All commands support the `--project` flag to specify which project to use:
```bash
# Sync a specific project
basic-memory --project=work sync
# Run MCP server for a specific project
basic-memory --project=personal mcp
```
You can also set the `BASIC_MEMORY_PROJECT` environment variable:
```bash
BASIC_MEMORY_PROJECT=work basic-memory sync
```
### Project Isolation
Each project maintains:
- Its own collection of markdown files in the specified directory
- A separate SQLite database for that project
- Complete knowledge graph isolation from other projects
## Workflow Tips
1. Run sync in watch mode for automatic updates
2. Use git for version control of your knowledge base
3. Review and edit AI-created content for accuracy
4. Periodically organize and refine your knowledge structure
5. Build rich connections between related ideas
6. Use forward references to plan future documentation
7. Start conversations with special prompts to leverage existing knowledge
## Troubleshooting
### Sync Issues
If changes aren't showing up:
1. Verify `basic-memory sync --watch` is running
2. Run `basic-memory status` to check system state
3. Try a manual sync with `basic-memory sync`
### Missing Content
If content isn't found:
1. Check the exact path and permalink
2. Try searching with more general terms
3. Verify the file exists in your knowledge base
### Relation Problems
If relations aren't working:
1. Ensure exact title matching in [[WikiLinks]]
2. Check for typos in relation types
3. Verify both documents exist
## Relations
- implements [[Knowledge Format]] (How knowledge is structured)
- relates_to [[Getting Started with Basic Memory]] (Setup and first steps)
- relates_to [[Canvas]] (Creating visual knowledge maps)
- relates_to [[CLI Reference]] (Command line tools)
+62
View File
@@ -0,0 +1,62 @@
---
title: Introduction to Basic Memory
type: docs
permalink: docs/introduction
tags:
- documentation
- index
- overview
---
# BASIC MEMORY
Basic Memory is a knowledge management system that allows you to build a persistent semantic graph from conversations
with AI assistants. All knowledge is stored in standard Markdown files on your computer, giving you full control and
ownership of your data.
Basic Memory connects you and AI assistants through shared knowledge:
1. **Captures knowledge** from natural conversations with AI assistants
2. **Structures information** using simple semantic patterns in Markdown
3. **Enables knowledge reuse** across different conversations and sessions
4. **Maintains persistence** through local files you control completely
Both you and AI assistants like Claude can read from and write to the same knowledge base, creating a continuous
learning environment where each conversation builds upon previous ones.
![[Obsidian-CoffeeKnowledgeBase-examples-overlays.gif]]
Basic Memory uses:
- **Files as the source of truth** - Everything is stored in plain Markdown files
- **Git-compatible storage** - All knowledge can be versioned, branched, and merged
- **Local SQLite database** - For fast indexing and searching only (not primary storage)
- **Model Context Protocol (MCP)** - For seamless AI assistant integration
Basic Memory gives you complete control over your knowledge:
- **Local-first storage** - All knowledge lives on your computer
- **Standard file formats** - Plain Markdown compatible with any editor
- **Directory organization** - Knowledge stored in `~/basic-memory` by default
- **Version control ready** - Use git for history, branching, and collaboration
- **Edit anywhere** - Modify files with any text editor or Obsidian
Changes to files automatically sync with the knowledge graph, and AI assistants can see your edits in conversations.
## Documentation Map
Continue exploring Basic Memory with these guides:
- Installation and setup [[Getting Started with Basic Memory]]
- Comprehensive usage instructions [[User Guide]]
- Detailed explanation of knowledge structure [[Knowledge Format]]
- Obsidian integration guide [[Obsidian Integration]]
- Canvas visualization guide [[Canvas]]
- Command line tool reference [[CLI Reference]]
- Reference for AI assistants using Basic Memory [[AI Assistant Guide]]
- Technical implementation details [[Technical Information]]
## Next Steps
Start with the [[Getting Started with Basic Memory]] guide to install Basic Memory and configure it with your AI
assistant.
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

-241
View File
@@ -1,241 +0,0 @@
# Character Handling and Conflict Resolution
Basic Memory handles various character encoding scenarios and file naming conventions to provide consistent permalink generation and conflict resolution. This document explains how the system works and how to resolve common character-related issues.
## Overview
Basic Memory uses a sophisticated system to generate permalinks from file paths while maintaining consistency across different operating systems and character encodings. The system normalizes file paths and generates unique permalinks to prevent conflicts.
## Character Normalization Rules
### 1. Permalink Generation
When Basic Memory processes a file path, it applies these normalization rules:
```
Original: "Finance/My Investment Strategy.md"
Permalink: "finance/my-investment-strategy"
```
**Transformation process:**
1. Remove file extension (`.md`)
2. Convert to lowercase (case-insensitive)
3. Replace spaces with hyphens
4. Replace underscores with hyphens
5. Handle international characters (transliteration for Latin, preservation for non-Latin)
6. Convert camelCase to kebab-case
### 2. International Character Support
**Latin characters with diacritics** are transliterated:
- `ø``o` (Søren → soren)
- `ü``u` (Müller → muller)
- `é``e` (Café → cafe)
- `ñ``n` (Niño → nino)
**Non-Latin characters** are preserved:
- Chinese: `中文/测试文档.md``中文/测试文档`
- Japanese: `日本語/文書.md``日本語/文書`
## Common Conflict Scenarios
### 1. Hyphen vs Space Conflicts
**Problem:** Files with existing hyphens conflict with generated permalinks from spaces.
**Example:**
```
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug" (CONFLICT!)
```
**Resolution:** The system automatically resolves this by adding suffixes:
```
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug-1"
```
**Best Practice:** Choose consistent naming conventions within your project.
### 2. Case Sensitivity Conflicts
**Problem:** Different case variations that normalize to the same permalink.
**Example on macOS:**
```
Directory: Finance/investment.md
Directory: finance/investment.md (different on filesystem, same permalink)
```
**Resolution:** Basic Memory detects case conflicts and prevents them during sync operations with helpful error messages.
**Best Practice:** Use consistent casing for directory and file names.
### 3. Character Encoding Conflicts
**Problem:** Different Unicode normalizations of the same logical character.
**Example:**
```
File 1: "café.md" (é as single character)
File 2: "café.md" (e + combining accent)
```
**Resolution:** Basic Memory normalizes Unicode characters using NFD normalization to detect these conflicts.
### 4. Forward Slash Conflicts
**Problem:** Forward slashes in frontmatter or file names interpreted as path separators.
**Example:**
```yaml
---
permalink: finance/investment/strategy
---
```
**Resolution:** Basic Memory validates frontmatter permalinks and warns about path separator conflicts.
## Error Messages and Troubleshooting
### "UNIQUE constraint failed: entity.file_path, entity.project_id"
**Cause:** Two entities trying to use the same file path within a project.
**Common scenarios:**
1. File move operation where destination is already occupied
2. Case sensitivity differences on macOS
3. Character encoding conflicts
4. Concurrent file operations
**Resolution steps:**
1. Check for duplicate file names with different cases
2. Look for files with similar names but different character encodings
3. Rename conflicting files to have unique names
4. Run sync again after resolving conflicts
### "File path conflict detected during move"
**Cause:** Enhanced conflict detection preventing potential database integrity violations.
**What this means:** The system detected that moving a file would create a conflict before attempting the database operation.
**Resolution:** Follow the specific guidance in the error message, which will indicate the type of conflict detected.
## Best Practices
### 1. File Naming Conventions
**Recommended patterns:**
- Use consistent casing (prefer lowercase)
- Use hyphens instead of spaces for multi-word files
- Avoid special characters that could conflict with path separators
- Be consistent with directory structure casing
**Examples:**
```
✅ Good:
- finance/investment-strategy.md
- projects/basic-memory-features.md
- docs/api-reference.md
❌ Problematic:
- Finance/Investment Strategy.md (mixed case, spaces)
- finance/Investment Strategy.md (inconsistent case)
- docs/API/Reference.md (mixed case directories)
```
### 2. Permalink Management
**Custom permalinks in frontmatter:**
```yaml
---
type: knowledge
permalink: custom-permalink-name
---
```
**Guidelines:**
- Use lowercase permalinks
- Use hyphens for word separation
- Avoid path separators unless creating sub-paths
- Ensure uniqueness within your project
### 3. Directory Structure
**Consistent casing:**
```
✅ Good:
finance/
investment-strategies.md
portfolio-management.md
❌ Problematic:
Finance/ (capital F)
investment-strategies.md
finance/ (lowercase f)
portfolio-management.md
```
## Migration and Cleanup
### Identifying Conflicts
Use Basic Memory's built-in conflict detection:
```bash
# Sync will report conflicts
basic-memory sync
# Check sync status for warnings
basic-memory status
```
### Resolving Existing Conflicts
1. **Identify conflicting files** from sync error messages
2. **Choose consistent naming convention** for your project
3. **Rename files** to follow the convention
4. **Re-run sync** to verify resolution
### Bulk Renaming Strategy
For projects with many conflicts:
1. **Backup your project** before making changes
2. **Standardize on lowercase** file and directory names
3. **Replace spaces with hyphens** in file names
4. **Use consistent character encoding** (UTF-8)
5. **Test sync after each batch** of changes
## System Enhancements
### Recent Improvements (v0.13+)
1. **Enhanced conflict detection** before database operations
2. **Improved error messages** with specific resolution guidance
3. **Character normalization utilities** for consistent handling
4. **File swap detection** for complex move scenarios
5. **Proactive conflict warnings** during permalink resolution
### Monitoring and Logging
The system now provides detailed logging for conflict resolution:
```
DEBUG: Detected potential file path conflicts for 'Finance/Investment.md': ['finance/investment.md']
WARNING: File path conflict detected during move: entity_id=123 trying to move from 'old.md' to 'new.md'
```
These logs help identify and resolve conflicts before they cause sync failures.
## Support and Resources
If you encounter character-related conflicts not covered in this guide:
1. **Check the logs** for specific conflict details
2. **Review error messages** for resolution guidance
3. **Report issues** with examples of the conflicting files
4. **Consider the file naming best practices** outlined above
The Basic Memory system is designed to handle most character conflicts automatically while providing clear guidance for manual resolution when needed.
-1046
View File
File diff suppressed because it is too large Load Diff
-91
View File
@@ -1,91 +0,0 @@
# Cloud Semantic Search Value (Customer-Facing Technical Story)
This document explains why teams should buy cloud semantic search even when local search exists.
## Core Promise
Markdown files remain the source of truth in both local and cloud modes.
- Files are portable.
- Search indexes are derived and rebuildable.
- You never get locked into proprietary document storage.
## The Customer Problem
Teams paying for cloud are usually not optimizing for "can this run locally." They are optimizing for:
- finding the right note the first time,
- keeping retrieval quality high as note volume grows,
- avoiding search slowdowns while content is actively changing,
- getting consistent results across users, agents, and sessions.
## Why Cloud Is the Aspirin
Cloud semantic search is the immediate pain reliever because it fixes the problems users feel right now.
### 1) Better hit rate on real queries
Cloud uses stronger managed embeddings than the default local model, which improves semantic recall for paraphrases and vague questions.
Customer outcome:
- fewer "I know this exists but search missed it" moments,
- less query rewording,
- faster time to answer.
### 2) Better behavior under active workloads
Cloud indexing runs out of band in workers, so indexing does not compete with interactive read/write traffic.
Customer outcome:
- stable search responsiveness during heavy updates,
- fresher semantic results shortly after edits,
- less user-visible performance variance.
### 3) Better consistency for shared knowledge
Cloud retrieval runs against a centralized tenant index, so teams and agents resolve against the same semantic state.
Customer outcome:
- fewer "works on my machine" search differences,
- more predictable agent behavior across environments,
- easier cross-user collaboration on large knowledge bases.
### 4) Better quality at higher scale
With Postgres + `pgvector` per tenant, cloud can sustain larger note collections and higher query volumes than typical local setups.
Customer outcome:
- confidence as repositories grow to tens of thousands of notes,
- less need for user-side tuning,
- fewer quality regressions as usage increases.
## Local Is the Vitamin
Local semantic search still matters and should stay strong.
- offline use,
- privacy-first operation,
- no cloud dependency,
- user-controlled runtime.
It compounds long-term ownership and resilience, but does not remove the immediate pain points cloud solves for teams at scale.
## Recommended Messaging
One-liner:
"Cloud semantic search is the aspirin: it fixes retrieval quality and performance pain now. Local semantic search is the vitamin: it builds long-term control and resilience."
Long form:
"Basic Memory keeps markdown as the source of truth everywhere. Local gives privacy and offline control. Cloud adds immediate, measurable improvements in search quality, consistency, and responsiveness for teams and agents running at scale."
## Packaging Guidance
- Base: local FTS plus optional local semantic search.
- Cloud value: higher semantic quality, stable performance under load, and consistent team-wide retrieval.
- Keep interfaces pluggable (`EmbeddingProvider`, vector backend protocol) so implementation can evolve without changing user workflows.
-300
View File
@@ -1,300 +0,0 @@
# LiteLLM Provider
Basic Memory can use the LiteLLM SDK for semantic search embeddings. This lets you
keep Basic Memory's vector indexing and search behavior while routing embedding calls
to OpenAI-compatible and provider-specific backends such as OpenAI, Azure OpenAI,
Cohere, Bedrock, NVIDIA NIM, and other LiteLLM-supported embedding providers.
Use this page when you want to try a non-default embedding model, validate a provider,
or tune LiteLLM-specific settings.
> **Experimental — advanced users only.** The LiteLLM provider is experimental and
> intended for users who are comfortable operating remote embedding backends. It makes
> paid, networked API calls, requires per-model dimension and input-role configuration,
> and reindexing a real corpus can be slow and spend provider quota (see
> [Reindexing with a remote provider](#reindexing-with-a-remote-provider)). For most
> users, the default local **FastEmbed** provider is the recommended choice. Use LiteLLM
> only if you know what you're doing.
## Quick Start
The default LiteLLM model is OpenAI `text-embedding-3-small` through the LiteLLM
model string `openai/text-embedding-3-small`.
```bash
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=litellm
export OPENAI_API_KEY=sk-...
bm reindex --embeddings
```
Then use vector or hybrid search:
```python
search_notes("login token flow", search_type="hybrid")
```
## Basic Memory Options
All options can be set in config or as environment variables.
| Config Field | Env Var | Default | Notes |
|---|---|---|---|
| `semantic_search_enabled` | `BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED` | Auto | Set to `true` to force vector/hybrid support on. |
| `semantic_embedding_provider` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER` | `fastembed` | Set to `litellm` for the LiteLLM provider. |
| `semantic_embedding_model` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL` | `bge-small-en-v1.5` | With `litellm`, the default is remapped to `openai/text-embedding-3-small`. |
| `semantic_embedding_dimensions` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_DIMENSIONS` | Provider default | Required for non-default LiteLLM models because vector tables are dimensioned before the first API call. |
| `semantic_embedding_forward_dimensions` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_FORWARD_DIMENSIONS` | Auto | Sends `dimensions` to LiteLLM only when supported. Auto is enabled for `text-embedding-3` model strings. |
| `semantic_embedding_document_input_type` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_DOCUMENT_INPUT_TYPE` | Auto | LiteLLM `input_type` for indexed notes/passages. |
| `semantic_embedding_query_input_type` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_QUERY_INPUT_TYPE` | Auto | LiteLLM `input_type` for search queries. |
| `semantic_embedding_batch_size` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_BATCH_SIZE` | `2` | Number of text chunks per provider request. |
| `semantic_embedding_request_concurrency` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_REQUEST_CONCURRENCY` | `4` | Maximum concurrent LiteLLM embedding requests. |
| `semantic_embedding_sync_batch_size` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_SYNC_BATCH_SIZE` | `2` | Number of prepared vector jobs flushed through the sync pipeline together. |
## Dimensions
Basic Memory needs the vector dimension before it can create SQLite or Postgres
vector tables. The OpenAI default is known, so this works without an explicit
dimension:
```bash
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=litellm
export BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL=openai/text-embedding-3-small
```
For every other LiteLLM model, set the dimension explicitly:
```bash
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=litellm
export BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL=cohere/embed-english-v3.0
export BASIC_MEMORY_SEMANTIC_EMBEDDING_DIMENSIONS=1024
```
For fixed-size models, `semantic_embedding_dimensions` is Basic Memory's local
schema and validation size. For OpenAI/Azure `text-embedding-3` models, LiteLLM
can also forward `dimensions` as a provider-side reduced-output request. Basic
Memory enables that automatically when the model string contains `text-embedding-3`.
If you use an Azure deployment alias such as `azure/<deployment-name>`, the model
string may not reveal that the underlying model supports reduced output dimensions.
Set this only when your deployment supports it:
```bash
export BASIC_MEMORY_SEMANTIC_EMBEDDING_FORWARD_DIMENSIONS=true
```
## Asymmetric Models
Some embedding models use different request roles for indexed documents and
search queries. Basic Memory automatically sets these for known LiteLLM families:
| Model Family | Document `input_type` | Query `input_type` |
|---|---|---|
| Cohere v3 embeddings | `search_document` | `search_query` |
| NVIDIA NIM retrieval embeddings | `passage` | `query` |
For any other asymmetric model, configure both roles explicitly:
```bash
export BASIC_MEMORY_SEMANTIC_EMBEDDING_DOCUMENT_INPUT_TYPE=passage
export BASIC_MEMORY_SEMANTIC_EMBEDDING_QUERY_INPUT_TYPE=query
```
Changing provider, model, dimensions, dimension-forwarding, or document/query
roles changes the meaning of stored vectors. Rebuild embeddings after any of
those changes:
```bash
bm reindex --embeddings
```
## Reindexing with a remote provider
Embedding a real corpus through a network API is far slower than local FastEmbed, and
the defaults are tuned for the local case. Two things to know before you run a full
reindex.
**Raise the sync batch size.** `semantic_embedding_sync_batch_size` defaults to `2`, and
it — not `semantic_embedding_batch_size` — governs throughput on the sync pipeline. With
the default, a full reindex can take tens of seconds *per note* against a remote provider.
Raising both to a larger value turns a multi-minute (or longer) reindex into well under a
minute for the same corpus:
```bash
export BASIC_MEMORY_SEMANTIC_EMBEDDING_SYNC_BATCH_SIZE=32
export BASIC_MEMORY_SEMANTIC_EMBEDDING_BATCH_SIZE=64
```
Stay within the provider's per-request size and rate limits — Cohere v3, for example,
accepts up to 96 inputs per embedding request.
**Changing dimensions requires recreating the vector table.** Basic Memory dimensions the
vector table on first index and refuses to mix sizes. Switching to a model with a
different dimension (for example FastEmbed 384 → OpenAI 1536 → Cohere 1024) makes a plain
`bm reindex` raise an `Embedding dimension mismatch` error. Recreate the table with a full
rebuild — files are the source of truth, so this re-indexes from disk and re-embeds
everything:
```bash
bm reset --reindex
```
To trial a provider without disturbing your existing index, point Basic Memory at a
throwaway config + database instead:
```bash
export BASIC_MEMORY_CONFIG_DIR=/tmp/bm-litellm-trial
```
## Provider Setup Examples
LiteLLM reads provider credentials from the environment. These are the examples
covered by Basic Memory's live validation harness.
### OpenAI Through LiteLLM
```bash
export OPENAI_API_KEY=sk-...
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=litellm
export BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL=openai/text-embedding-3-small
```
### Cohere v3
```bash
export COHERE_API_KEY=...
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=litellm
export BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL=cohere/embed-english-v3.0
export BASIC_MEMORY_SEMANTIC_EMBEDDING_DIMENSIONS=1024
```
The provider auto-selects `search_document` for indexed chunks and `search_query`
for search queries.
### Azure OpenAI
```bash
export AZURE_API_KEY=...
export AZURE_API_BASE=https://<resource-name>.openai.azure.com
export AZURE_API_VERSION=2024-02-01
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=litellm
export BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL=azure/<deployment-name>
export BASIC_MEMORY_SEMANTIC_EMBEDDING_DIMENSIONS=1536
```
If your Azure deployment is a reduced-dimension `text-embedding-3` deployment,
set the dimension you want and enable forwarding:
```bash
export BASIC_MEMORY_SEMANTIC_EMBEDDING_DIMENSIONS=512
export BASIC_MEMORY_SEMANTIC_EMBEDDING_FORWARD_DIMENSIONS=true
```
### NVIDIA NIM
```bash
export NVIDIA_NIM_API_KEY=...
# Optional when using a custom or self-hosted NIM endpoint:
export NVIDIA_NIM_API_BASE=https://integrate.api.nvidia.com/v1
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=litellm
export BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL=nvidia_nim/nvidia/embed-qa-4
export BASIC_MEMORY_SEMANTIC_EMBEDDING_DIMENSIONS=1024
```
The provider auto-selects `passage` for indexed chunks and `query` for search
queries.
## Testing LiteLLM Providers
Run the non-live LiteLLM unit and harness tests first:
```bash
uv run pytest tests/repository/test_litellm_provider.py \
test-int/semantic/test_litellm_live_harness.py -q
```
Run the SQLite and Postgres vector identity regressions when changing model
identity, role, or vector sync behavior:
```bash
uv run pytest \
tests/repository/test_sqlite_vector_search_repository.py::test_sqlite_embedding_model_key_includes_litellm_role_settings \
-q
BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest \
tests/repository/test_postgres_search_repository.py::test_postgres_litellm_role_change_reembeds_existing_chunks \
-q
```
The Postgres command uses testcontainers, so Docker must be running.
## Live Provider Harness
The live harness makes real LiteLLM API calls and spends provider quota. It is
opt-in by design:
```bash
export OPENAI_API_KEY=sk-...
export COHERE_API_KEY=...
just test-litellm-live
```
Built-in cases run when their API keys are present:
| Case | Required Env Var | Validates |
|---|---|---|
| `openai-text-embedding-3-small` | `OPENAI_API_KEY` | OpenAI via LiteLLM, 1536 dimensions, normalized vectors, ranking sanity. |
| `cohere-embed-english-v3` | `COHERE_API_KEY` | Cohere v3 role handling, 1024 dimensions, normalized vectors, ranking sanity. |
Add provider aliases or new backends with a custom cases file:
```bash
cat > /tmp/litellm-cases.json <<'JSON'
[
{
"name": "azure-text-embedding-3-small-512",
"model": "azure/<deployment-name>",
"dimensions": 512,
"api_key_env": "AZURE_API_KEY",
"forward_dimensions": true
},
{
"name": "nvidia-embed-qa-4",
"model": "nvidia_nim/nvidia/embed-qa-4",
"dimensions": 1024,
"api_key_env": "NVIDIA_NIM_API_KEY",
"document_input_type": "passage",
"query_input_type": "query"
}
]
JSON
just test-litellm-live --cases-file /tmp/litellm-cases.json
```
For CI-style output:
```bash
just test-litellm-live --cases-file /tmp/litellm-cases.json --json
```
The harness embeds two documents and one query, validates dimension and vector
normalization, checks that the authentication query ranks the authentication
document above a distractor, and reports latency plus role/dimension settings.
## Provider Reference
LiteLLM's own provider and embedding docs are the source of truth for current
model strings and credential names:
- [LiteLLM embedding models](https://docs.litellm.ai/docs/embedding/supported_embedding)
- [LiteLLM Azure OpenAI provider](https://docs.litellm.ai/docs/providers/azure)
- [LiteLLM NVIDIA NIM provider](https://docs.litellm.ai/docs/providers/nvidia_nim)
-499
View File
@@ -1,499 +0,0 @@
# Logfire Instrumentation Strategy
## Why
We want Logfire in Basic Memory for two specific use cases:
1. Local development and performance investigation
2. Cloud deployments where Basic Memory runs inside Basic Memory Cloud
This instrumentation must be:
- Disabled by default
- Useful when enabled
- Safe for local-first users
- Searchable in Logfire over time
The previous integration added telemetry, but it leaned too much on generic framework instrumentation. That created noisy spans with weak names and made the trace view harder to navigate. This strategy favors manual instrumentation around Basic Memory's real units of work.
## Core Principles
### 1. Default-off
Basic Memory should ship with Logfire disabled unless the operator explicitly enables it.
That means:
- no required token for normal local usage
- no surprise outbound telemetry
- no behavior change for existing users
### 2. Manual spans over automatic framework spans
We should not rely on broad auto-instrumentation for FastAPI, MCP, SQLAlchemy, or HTTP as the primary experience.
Why:
- auto-generated span names are often generic
- routes and middleware produce too many low-signal spans
- it becomes harder to answer product questions like "why was `write_note` slow?" or "where did sync time go?"
The preferred model is:
- one meaningful root span per high-level operation
- a small number of child spans for important phases
- optional targeted instrumentation only where it adds clear value
### 3. Logs must live inside traces
Basic Memory already uses `loguru` pervasively. The Logfire integration should preserve that and make those logs visible inside the active trace/span context.
If traces exist but the logs are detached from them, the integration is not doing its job.
### 4. Stable names, selective attributes
Span names should describe the operation class, not the specific input.
Good:
- `mcp.tool.write_note`
- `sync.project.scan`
- `search.execute`
- `routing.resolve_project`
Bad:
- `Searching for "foo bar baz"`
- `POST /v2/projects/123/search/`
- `write note to /specs/api.md`
Dynamic values belong in attributes, not in the span name.
## What We Should Not Do
### Avoid broad FastAPI auto-instrumentation
We should not turn on `instrument_fastapi()` and treat that as the main telemetry story.
It may still be useful in narrowly scoped debugging, but it should not define the production trace shape. The meaningful root spans should come from Basic Memory's own entrypoints and service boundaries.
### Avoid per-file spans by default
`sync` can process many files. A span per file will explode trace cardinality and make performance views noisy.
Default behavior should be:
- one span for the project sync
- child spans for scan, move handling, delete handling, markdown sync batch, relation resolution, embedding sync, watermark update
- per-file spans only for failures or very slow outliers
### Avoid high-cardinality attributes on every span
Do not attach large or highly variable values everywhere:
- raw note content
- file bodies
- long search text
- arbitrary metadata blobs
- unique IDs that make every span shape distinct
Prefer compact, queryable attributes:
- `project_name`
- `workspace_id`
- `route_mode`
- `scan_type`
- `file_count`
- `result_count`
- `search_type`
- `retrieval_mode`
- `duration_ms`
## Proposed Architecture
Add a dedicated telemetry module in core Basic Memory, separate from logging setup.
Suggested shape:
```python
# basic_memory/telemetry.py
def configure_telemetry(service_name: str, *, enable_logfire: bool) -> None: ...
def telemetry_enabled() -> bool: ...
def span(name: str, **attrs): ...
def bind_telemetry_context(**attrs): ...
```
This module should:
- configure Logfire only when explicitly enabled
- set up the Logfire `loguru` handler
- expose lightweight helpers so application code does not import `logfire` directly everywhere
- degrade cleanly to no-op behavior when disabled
This keeps the rest of the codebase readable and makes it easy to reason about what telemetry is doing.
## Logging Integration Strategy
### Goal
When a span is active, logs emitted through `loguru` during that operation should show up in the same trace.
### Preferred design
1. Configure Logfire once in the telemetry bootstrap
2. Add the Logfire `loguru` handler to the existing `loguru` configuration
3. At operation boundaries, bind stable contextual fields with `loguru`
4. Let logs emitted inside the span inherit the active trace context
### Context to bind
Bind only the fields that help correlate work across the system:
- `service_name`
- `entrypoint`
- `project_name`
- `workspace_id`
- `route_mode`
- `tool_name`
- `command_name`
This binding should happen at the root of an operation, not deep in leaf functions.
### Important nuance
We should not try to encode the entire trace model into logger extras. The logger context should be a human-meaningful slice of the active operation. Trace linkage comes from the active Logfire/OpenTelemetry context; logger extras are there to improve searchability and readability.
## Span Model
### Root spans
Each user-visible or system-visible operation should get one root span.
Examples:
- `cli.command.status`
- `cli.command.project_sync`
- `api.request.search`
- `mcp.tool.write_note`
- `mcp.tool.read_note`
- `mcp.tool.search_notes`
- `sync.project.run`
- `db.semantic_backfill`
### Child spans
Child spans should represent real phases whose duration we care about.
Examples:
- `routing.client_session`
- `routing.resolve_project`
- `routing.resolve_workspace`
- `api.search.execute`
- `sync.project.scan`
- `sync.project.detect_moves`
- `sync.project.apply_changes`
- `sync.project.resolve_relations`
- `sync.project.sync_embeddings`
- `sync.file.markdown`
- `sync.file.regular`
- `search.execute`
- `search.relaxed_fts_retry`
- `db.init`
- `db.migrate`
### Span naming rules
- Use dot-separated names
- Start with subsystem
- Keep the verb at the end
- Keep names stable across runs
- Never include request-specific text in the span name
## Attribute Taxonomy
### Required attributes on root spans
Every root span should have a small common set:
- `service_name`
- `entrypoint`
- `project_name` when applicable
- `workspace_id` when applicable
- `route_mode` with values like `local_asgi`, `cloud_proxy`, `factory`
### Operation-specific attributes
Examples:
For search:
- `search_type`
- `retrieval_mode`
- `page`
- `page_size`
- `result_count`
- `fallback_used`
For sync:
- `scan_type`
- `force_full`
- `new_count`
- `modified_count`
- `deleted_count`
- `move_count`
- `skipped_count`
- `embeddings_enabled`
For note operations:
- `tool_name`
- `note_type`
- `directory`
- `overwrite`
- `output_format`
### Attributes to avoid by default
- full `query.text`
- full note titles if they create privacy or cardinality issues
- file content
- raw frontmatter
- raw HTTP bodies
If we need richer payloads for a local debugging session, that should be an explicit temporary mode, not the default telemetry shape.
## Instrumentation Plan By Layer
### 1. Entrypoints
Instrument these first:
- `cli.app` callback and major commands
- API lifespan and selected routers
- MCP server lifespan
- MCP tool entrypoints
Why:
- this establishes clean root spans
- it gives us trace boundaries that match how users think about the product
### 2. Routing and context resolution
Instrument:
- client routing decisions
- workspace resolution
- project resolution
- default-project fallback
Why:
- Basic Memory has local/cloud/per-project routing logic
- when something is slow or surprising, we need to know which path was taken
### 3. Sync and indexing
This is the highest-value area to instrument deeply.
Instrument:
- sync root
- scan strategy decision
- filesystem scan
- move detection
- delete handling
- markdown sync phase
- relation resolution
- vector embedding sync
- scan watermark update
Why:
- this is where performance work will happen
- cloud and local both benefit from this visibility
### 4. Search
Instrument:
- search execution
- retrieval mode
- relaxed FTS fallback
- result shaping
Why:
- search is user-facing and latency-sensitive
- hybrid/vector/FTS paths need to be distinguishable
### 5. Database and initialization
Instrument selectively:
- DB init
- migrations
- semantic backfill
- connection mode selection
Avoid full automatic SQL span firehose by default.
## Recommended Rollout Phases
## Task List
- [x] Phase 1: Bootstrap and config gating
- [x] Phase 2: Root spans for entrypoints and primary operations
- [x] Phase 3: Child spans for sync, search, and routing
- [x] Phase 4: Failure-focused detail and final verification
- [x] Phase 5: Loguru context binding and scoped context inheritance
## Recommended Rollout Phases
### Phase 1: Bootstrap and config gating
Add:
- telemetry bootstrap module
- config/env gating
- `loguru` + Logfire handler integration
This gives immediate value with low noise.
### Phase 2: Root spans for entrypoints and primary operations
Add:
- root spans for CLI, API, MCP, and main MCP tools
- stable root attributes for project, workspace, route mode, and operation type
This gives us clean top-level traces that match how users think about the product.
### Phase 3: Child spans for sync, search, and routing
Add child spans to:
- sync
- search
- routing
This is the main performance-investigation layer.
### Phase 4: Failure-focused detail
Add selective deeper spans/log enrichment for:
- sync failures
- relation resolution failures
- slow file operations
- cloud routing/auth failures
This keeps normal traces clean while improving debuggability.
### Phase 5: Loguru context binding and scoped context inheritance
Add:
- context-local telemetry state in `basic_memory.telemetry`
- a shared `scope(...)` helper that opens a span and binds stable logger context together
- context inheritance for routing, sync, and search so downstream `loguru` logs carry the active operation fields
This makes the trace view and the log stream tell the same story without forcing logger rewrites across the codebase.
## Local Dev Playbook
The fastest way to sanity-check the current trace shape is:
```bash
LOGFIRE_TOKEN=lf_... just telemetry-smoke
```
What this does:
- creates an isolated temp home, config dir, and project path
- enables Logfire for the run
- automatically exports to Logfire when `LOGFIRE_TOKEN` is present
- defaults `BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=false` so the smoke run stays fast and trace-friendly
- disables promo telemetry so the trace is about Basic Memory work, not analytics noise
- runs a small CLI workflow:
- `project add`
- `tool write-note`
- `tool read-note`
- `tool edit-note`
- `tool build-context`
- `tool search-notes`
- `doctor`
If you want to exercise the instrumentation without exporting anything upstream:
```bash
BASIC_MEMORY_LOGFIRE_SEND_TO_LOGFIRE=false just telemetry-smoke
```
If you want the smoke run to include vector or hybrid retrieval spans too:
```bash
LOGFIRE_TOKEN=lf_... BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true just telemetry-smoke
```
The recipe sets `BASIC_MEMORY_LOGFIRE_ENVIRONMENT=telemetry-smoke` by default so these traces are easy to isolate in Logfire. Override it if you want the smoke traces grouped under a different environment name.
### What to look for
You should see a small set of comparable root spans rather than a framework-generated span forest:
- `cli.command.project`
- `cli.command.tool`
- `mcp.tool.write_note`
- `mcp.tool.read_note`
- `mcp.tool.edit_note`
- `mcp.tool.build_context`
- `mcp.tool.search_notes`
- `sync.project.run`
You should also see correlated logs under those traces with stable fields like:
- `project_name`
- `route_mode`
- `tool_name`
- `entrypoint`
### Expected nuance
`doctor` creates its own temporary project on purpose. That means the sync trace will usually show a different project name than the `telemetry-smoke` write/search traces. That is fine for smoke testing because the goal is to confirm:
- root span names are meaningful
- scoped logs stay attached to the active trace
- routing, tool, search, and sync phases are easy to distinguish
## Validation Checklist
We should consider the integration successful when the following are true:
1. With telemetry disabled, Basic Memory behaves exactly as it does today.
2. With telemetry enabled, one user action produces one obvious root span.
3. Logs emitted during that action are visible inside the same trace.
4. A search in Logfire for `mcp.tool.write_note` or `sync.project.run` returns comparable spans across runs.
5. Trace views show phase timing clearly without drowning in framework noise.
6. Sensitive payloads are not captured by default.
## Immediate Implementation Direction
When we start coding, the first pass should be:
1. Add `basic_memory.telemetry`
2. Add config/env switches for `enabled`, `send_to_logfire`, and service name
3. Wire telemetry bootstrap into CLI, API, and MCP entrypoints
4. Configure `loguru` to emit to both existing sinks and the Logfire handler when enabled
5. Add manual root spans around:
- CLI commands
- API request handlers we care about
- MCP tool entrypoints
- sync root
- search root
6. Add child spans to the sync and routing phases only after the root span model feels clean
That gives us a strong foundation without repeating the earlier "turn on instrumentation everywhere" approach.
-138
View File
@@ -1,138 +0,0 @@
# MCP UI Bakeoff - Instructions & Test Plan
Last updated: 2026-02-02
## Scope
Compare three presentation paths for Basic Memory MCP tools:
1. **ToolUI (React)** via MCP App resources.
2. **MCPUI Python SDK** embedded UI resources (legacy host path).
3. **ASCII/ANSI** output for TUI clients.
This doc is the running instruction set and test plan. Update as implementation progresses.
---
## Prerequisites
- Repo: `basic-memory` (worktree: `basic-memory-mcp-ui-poc`)
- Node for toolui build (already used for POC)
- Python 3.12+ with `uv`
Optional (for MCPUI Python SDK path):
- Local repo: `/Users/phernandez/dev/mcp-ui`
- Install the server SDK into the Basic Memory venv:
- `uv pip install -e /Users/phernandez/dev/mcp-ui/sdks/python/server`
---
## Build / Refresh Steps
### ToolUI React bundle
```bash
cd ui/tool-ui-react
npm install
npm run build
```
This regenerates:
- `src/basic_memory/mcp/ui/html/search-results-tool-ui.html`
- `src/basic_memory/mcp/ui/html/note-preview-tool-ui.html`
---
## How to Run the MCP Server
```bash
basic-memory mcp --transport stdio
```
Optional to pick UI variant for MCP App resources:
```bash
export BASIC_MEMORY_MCP_UI_VARIANT=tool-ui # or vanilla | mcp-ui
```
---
## Test Cases
### 1) MCP App Resource UI (toolui / vanilla / mcpui)
Tools:
- `search_notes`
- `read_note`
Expect:
- Tool meta points to `ui://basic-memory/search-results` and `ui://basic-memory/note-preview`
- Resource content differs by `BASIC_MEMORY_MCP_UI_VARIANT`
- Variantspecific URIs also available:
- `ui://basic-memory/search-results/vanilla`
- `ui://basic-memory/search-results/tool-ui`
- `ui://basic-memory/search-results/mcp-ui`
- `ui://basic-memory/note-preview/vanilla`
- `ui://basic-memory/note-preview/tool-ui`
- `ui://basic-memory/note-preview/mcp-ui`
Manual check:
- Trigger tool in MCPAppcapable host and confirm UI renders.
---
### 2) Text / JSON Output Modes
Tools:
- `search_notes(output_format="text" | "json")`
- `read_note(output_format="text" | "json")`
- `write_note(output_format="text" | "json")`
- `edit_note(output_format="text" | "json")`
- `recent_activity(output_format="text" | "json")`
- `list_memory_projects(output_format="text" | "json")`
- `create_memory_project(output_format="text" | "json")`
- `delete_note(output_format="text" | "json")`
- `move_note(output_format="text" | "json")`
- `build_context(output_format="json" | "text")`
Expect:
- `text` mode preserves existing human-readable responses.
- `json` mode returns structured dict/list payloads for machine-readable clients.
Automated:
- `uv run pytest test-int/mcp/test_output_format_json_integration.py`
---
### 3) MCPUI Python SDK (embedded UI resource)
Tools (embedded resource responses):
- `search_notes_ui` (MCPUI SDK)
- `read_note_ui` (MCPUI SDK)
Expected output:
- Tool response content contains an EmbeddedResource (`type: "resource"`)
- `mimeType` is `text/html`
- `_meta` includes:
- `mcpui.dev/ui-preferred-frame-size`
- `mcpui.dev/ui-initial-render-data`
Manual check:
- Render tool responses using `UIResourceRenderer` (legacy host flow).
Automated (if SDK installed):
- `uv run pytest test-int/mcp/test_ui_sdk_integration.py`
---
## Bakeoff Notes Template
Fill in after running:
- ToolUI (React): __
- MCPUI SDK (embedded): __
- Text/JSON modes: __
Decision + rationale: __

Some files were not shown because too many files have changed in this diff Show More