Compare commits

..

31 Commits

Author SHA1 Message Date
semantic-release 9fe34dcddf chore(release): 0.10.0 [skip ci] 2025-03-15 20:49:20 +00:00
Paul Hernandez eb5e4ec6bd fix: improve utf-8 support for file reading/writing (#32)
fixes #29

Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 15:43:37 -05:00
Paul Hernandez 6b110b28dd fix: don't sync *.tmp files on watch (#31)
Fixes #30

Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 15:43:28 -05:00
phernandez 88d193e0aa Edig content in README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 11:46:53 -05:00
phernandez fa9175f049 Remove redundant info in README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 11:45:28 -05:00
phernandez 7c787a413c Simpify README.md, ONTRIBUTING.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 11:31:18 -05:00
Paul Hernandez 0fa1adf3b5 Update README.md
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-03-15 11:17:29 -05:00
phernandez cbe488fbd2 remove .obsidian dir in docs, try to fix video
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 11:00:27 -05:00
Paul Hernandez 99ff8f60c2 Update README.md
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-03-15 10:57:59 -05:00
phernandez 06ee2852a0 try to fix video in README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 10:53:08 -05:00
phernandez 939bab1a24 fix demo link in README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 10:48:58 -05:00
phernandez 67736487fb update README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 10:43:41 -05:00
phernandez 2ac620f98b Merge branch 'main' of github.com:basicmachines-co/basic-memory 2025-03-15 10:40:16 -05:00
phernandez 4cbda7a9d4 update docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 10:40:09 -05:00
Paul Hernandez 3070ade69a Create dependabot.yml
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-03-15 10:34:53 -05:00
Paul Hernandez 9af913da4f docs: add glama badge. Fix typos in README.md (#28)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-13 13:24:39 -05:00
Paul Hernandez 7b7bec9557 Create SECURITY.md
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-03-12 15:51:23 -05:00
bm-claudeai fea2f40d1b docs: Update CLAUDE.md with GitHub integration capabilities (#25)
This PR updates the CLAUDE.md file to document the GitHub integration
capabilities that enable Claude to participate directly in the
development workflow.
2025-03-11 16:32:30 -05:00
bm-claudeai eb1e7b6088 feat: Add Smithery integration for easier installation (#24)
This PR adds support for deploying Basic Memory on the Smithery platform.

Signed-off-by: bm-claudeai <claude@basicmachines.co>
2025-03-11 16:21:51 -05:00
phernandez 9203730ba7 update README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-11 14:04:01 -05:00
phernandez 1a473c02e9 remove cool asii art from README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-11 14:00:37 -05:00
phernandez b6f5d8a545 update getting started docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-11 13:35:33 -05:00
phernandez addeeb1e7f update user docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-11 13:30:00 -05:00
phernandez 85f9099b0a update docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-08 11:53:24 -06:00
phernandez 82b6f3d60e update user guide docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-08 11:51:17 -06:00
phernandez da97353cfc fix: ai_resource_guide.md path
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-07 14:25:24 -06:00
phernandez 2e9d673e54 fix: ai_resource_guide.md path
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-07 14:24:48 -06:00
phernandez c4732a47b3 fix: ai_resource_guide.md path 2025-03-07 14:19:27 -06:00
phernandez 2d5176e800 add user guide docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-07 14:11:06 -06:00
phernandez 9bb8a020c3 chore: remove logfire
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-06 13:06:48 -06:00
phernandez 31cca6f913 fix: drop search_index table on db reindex
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-06 12:51:25 -06:00
42 changed files with 831 additions and 1071 deletions
+12
View File
@@ -0,0 +1,12 @@
# 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"
+5
View File
@@ -43,3 +43,8 @@ ENV/
# macOS
.DS_Store
/.coverage.*
# obsidian docs:
/docs/.obsidian/
/examples/.obsidian/
/examples/.basic-memory/
+70
View File
@@ -1,6 +1,76 @@
# CHANGELOG
## v0.10.0 (2025-03-15)
### Bug Fixes
- Ai_resource_guide.md path
([`da97353`](https://github.com/basicmachines-co/basic-memory/commit/da97353cfc3acc1ceb0eca22ac6af326f77dc199))
Signed-off-by: phernandez <paul@basicmachines.co>
- Ai_resource_guide.md path
([`c4732a4`](https://github.com/basicmachines-co/basic-memory/commit/c4732a47b37dd2e404139fb283b65556c81ce7c9))
- Ai_resource_guide.md path
([`2e9d673`](https://github.com/basicmachines-co/basic-memory/commit/2e9d673e54ad6a63a971db64f01fc2f4e59c2e69))
Signed-off-by: phernandez <paul@basicmachines.co>
- Don't sync *.tmp files on watch ([#31](https://github.com/basicmachines-co/basic-memory/pull/31),
[`6b110b2`](https://github.com/basicmachines-co/basic-memory/commit/6b110b28dd8ba705ebfc0bcb41faf2cb993da2c3))
Fixes #30
Signed-off-by: phernandez <paul@basicmachines.co>
- Drop search_index table on db reindex
([`31cca6f`](https://github.com/basicmachines-co/basic-memory/commit/31cca6f913849a0ab8fc944803533e3072e9ef88))
Signed-off-by: phernandez <paul@basicmachines.co>
- Improve utf-8 support for file reading/writing
([#32](https://github.com/basicmachines-co/basic-memory/pull/32),
[`eb5e4ec`](https://github.com/basicmachines-co/basic-memory/commit/eb5e4ec6bd4d2fe757087be030d867f4ca1d38ba))
fixes #29
Signed-off-by: phernandez <paul@basicmachines.co>
### Chores
- Remove logfire
([`9bb8a02`](https://github.com/basicmachines-co/basic-memory/commit/9bb8a020c3425a02cb3a88f6f02adcd281bccee2))
Signed-off-by: phernandez <paul@basicmachines.co>
### Documentation
- Add glama badge. Fix typos in README.md
([#28](https://github.com/basicmachines-co/basic-memory/pull/28),
[`9af913d`](https://github.com/basicmachines-co/basic-memory/commit/9af913da4fba7bb4908caa3f15f2db2aa03777ec))
Signed-off-by: phernandez <paul@basicmachines.co>
- Update CLAUDE.md with GitHub integration capabilities
([#25](https://github.com/basicmachines-co/basic-memory/pull/25),
[`fea2f40`](https://github.com/basicmachines-co/basic-memory/commit/fea2f40d1b54d0c533e6d7ee7ce1aa7b83ad9a47))
This PR updates the CLAUDE.md file to document the GitHub integration capabilities that enable
Claude to participate directly in the development workflow.
### Features
- Add Smithery integration for easier installation
([#24](https://github.com/basicmachines-co/basic-memory/pull/24),
[`eb1e7b6`](https://github.com/basicmachines-co/basic-memory/commit/eb1e7b6088b0b3dead9c104ee44174b2baebf417))
This PR adds support for deploying Basic Memory on the Smithery platform.
Signed-off-by: bm-claudeai <claude@basicmachines.co>
## v0.9.0 (2025-03-07)
### Chores
+39 -1
View File
@@ -130,4 +130,42 @@ of using AI just for code generation, we've developed a true collaborative workf
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.
could achieve independently.
## 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
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.
+38 -34
View File
@@ -1,6 +1,7 @@
# 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
@@ -65,61 +66,64 @@ Thank you for considering contributing to Basic Memory! This document outlines t
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
You can sign your commits in one of two ways:
Sign your commit:
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.
**Using the `-s` or `--signoff` flag**:
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.
```bash
git commit -s -m "Your commit message"
```
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.
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.
## Code Style Guidelines
@@ -135,10 +139,10 @@ The sign-off certifies that you have the right to submit your contribution under
- **Coverage Target**: We aim for 100% test coverage for all code
- **Test Framework**: Use pytest for unit and integration tests
- **Mocking**: Use pytest-mock for mocking dependencies
- **Mocking**: Use pytest-mock for mocking dependencies only when necessary
- **Edge Cases**: Test both normal operation and edge cases
- **Database Testing**: Use in-memory SQLite for testing database operations
- **Fixtures**: Use pytest fixtures for setup and teardown
- **Fixtures**: Use async pytest fixtures for setup and teardown
## Creating Issues
@@ -156,4 +160,4 @@ All contributors must follow the [Code of Conduct](CODE_OF_CONDUCT.md).
## Thank You!
Your contributions help make Basic Memory better for everyone. We appreciate your time and effort!
Your contributions help make Basic Memory better. We appreciate your time and effort!
+16
View File
@@ -0,0 +1,16 @@
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM python:3.12-slim
WORKDIR /app
# Copy the project files
COPY . .
# Install pip and build dependencies
RUN pip install --upgrade pip \
&& pip install . --no-cache-dir --ignore-installed
# Expose port if necessary (e.g., uv might use a port, but MCP over stdio so not needed here)
# Use the basic-memory entrypoint to run the MCP server
CMD ["basic-memory", "mcp"]
+42 -392
View File
@@ -1,38 +1,32 @@
# Basic Memory
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![PyPI version](https://badge.fury.io/py/basic-memory.svg)](https://badge.fury.io/py/basic-memory)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![Tests](https://github.com/basicmachines-co/basic-memory/workflows/Tests/badge.svg)](https://github.com/basicmachines-co/basic-memory/actions)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![smithery badge](https://smithery.ai/badge/@basicmachines-co/basic-memory)](https://smithery.ai/server/@basicmachines-co/basic-memory)
```
██████╗ █████╗ ███████╗██╗ ██████╗ ███╗ ███╗███████╗███╗ ███╗ ██████╗ ██████╗ ██╗ ██╗
██╔══██╗██╔══██╗██╔════╝██║██╔════╝ ████╗ ████║██╔════╝████╗ ████║██╔═══██╗██╔══██╗╚██╗ ██╔╝
██████╔╝███████║███████╗██║██║ ██╔████╔██║█████╗ ██╔████╔██║██║ ██║██████╔╝ ╚████╔╝
██╔══██╗██╔══██║╚════██║██║██║ ██║╚██╔╝██║██╔══╝ ██║╚██╔╝██║██║ ██║██╔══██╗ ╚██╔╝
██████╔╝██║ ██║███████║██║╚██████╗ ██║ ╚═╝ ██║███████╗██║ ╚═╝ ██║╚██████╔╝██║ ██║ ██║
╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
```
# Basic Memory
Basic Memory lets you build persistent knowledge through natural conversations with Large Language Models (LLMs) like
Claude, while keeping everything in simple Markdown files on your computer. It uses the Model Context Protocol (MCP) to
enable any compatible LLM to read and write to your local knowledge base.
LLMs can build context from local knowledge bases.
- Website: http://basicmachines.co
- Documentation: http://memory.basicmachines.co
![Example Gif](docs/attachments/Obsidian-CoffeeKnowledgeBase-examples-overlays.gif)
## Pick up your conversation right where you left off
Basic Memory provides persistent contextual awareness across sessions through a structured knowledge graph.
The system enables LLMs to access and reference prior conversations, track semantic relationships between concepts, and
incorporate human edits made directly to knowledge files.
- AI assistants can load context from local files in a new conversation
- Notes are saved locally as Markdown files in real time
- No project knowledge or special prompting required
https://github.com/user-attachments/assets/a55d8238-8dd0-454a-be4c-8860dbbd0ddc
## Quick Start
```bash
# Install with uv (recommended)
uv install basic-memory
uv tool install basic-memory
# Configure Claude Desktop (edit ~/Library/Application Support/Claude/claude_desktop_config.json)
# Add this to your config:
@@ -56,33 +50,23 @@ uv install basic-memory
You can view shared context via files in `~/basic-memory` (default directory location).
You can also install the cli tools to sync files or manage projects.
### Alternative Installation via Smithery
```bash
uv tool install basic-memory
You can use [Smithery](https://smithery.ai/server/@basicmachines-co/basic-memory) to automatically configure Basic
Memory for Claude Desktop:
# create a new project in a different directory
basic-memory project add coffee ./examples/coffee
# you can set the project to the default
basic-memory project default coffee
```bash
npx -y @smithery/cli install @basicmachines-co/basic-memory --client claude
```
View available projects
This installs and configures Basic Memory without requiring manual edits to the Claude Desktop configuration file. The
Smithery server hosts the MCP server component, while your data remains stored locally as Markdown files.
```bash
basic-memory project list
Basic Memory Projects
┏━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┓
┃ Name ┃ Path ┃ Default ┃ Active ┃
┡━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━┩
│ main │ ~/basic-memory │ ✓ │ ✓ │
│ coffee │ ~/dev/basicmachines/basic-memory/examples/coffee │ │ │
└────────┴──────────────────────────────────────────────────┴─────────┴────────┘
```
### Glama.ai
Basic Memory will write notes in Markdown format. Open you project directory in your text editor to view project files
while you have conversations with an LLM.
<a href="https://glama.ai/mcp/servers/o90kttu9ym">
<img width="380" height="200" src="https://glama.ai/mcp/servers/o90kttu9ym/badge" alt="basic-memory MCP server" />
</a>
## Why Basic Memory?
@@ -163,36 +147,13 @@ tags:
- affects [[Flavor Extraction]]
```
The note embeds semantic content and links to other topics via simple Markdown
formatting.
The note embeds semantic content and links to other topics via simple Markdown formatting.
3. You see this file on your computer in real time in the `~/$HOME/basic-memory` directory:
3. You see this file on your computer in real time in the current project directory (default `~/$HOME/basic-memory`).
```markdown
---
title: Coffee Brewing Methods
permalink: coffee-brewing-methods
type: note
---
- Realtime sync can be enabled via running `basic-memory sync --watch`
# Coffee Brewing Methods
## Observations
- [method] Pour over provides more clarity and highlights subtle flavors
- [technique] Water temperature at 205°F (96°C) extracts optimal compounds
- [principle] Freshly ground beans preserve aromatics and flavor
- [preference] Medium-light roasts work best for pour over # Added by you
## Relations
- relates_to [[Coffee Bean Origins]]
- requires [[Proper Grinding Technique]]
- affects [[Flavor Extraction]]
- pairs_with [[Breakfast Pastries]] # Added by you
```
4. In a new chat with the LLM, you can reference this knowledge:
4. In a chat with the LLM, you can reference a topic:
```
Look at `coffee-brewing-methods` for context about pour over coffee
@@ -212,15 +173,14 @@ Following relation 'requires [[Proper Grinding Technique]]':
- Impact of consistent particle size on extraction
```
Each related document can lead to more context, building a rich semantic understanding of your knowledge base. All of
this context comes from standard Markdown files that both humans and LLMs can read and write.
Each related document can lead to more context, building a rich semantic understanding of your knowledge base.
Every time the LLM writes notes,they are saved in local Markdown files that you can:
This creates a two-way flow where:
- Edit in any text editor
- Version via git
- Back up normally
- Share when you want to
- Humans write and edit Markdown files
- LLMs read and write through the MCP protocol
- Sync keeps everything consistent
- All knowledge stays in local files.
## Technical Implementation
@@ -256,7 +216,7 @@ permalink: <a uri slug>
Observations are facts about a topic.
They can be added by creating a Markdown list with a special format that can reference a `category`, `tags` using a
"#" charactor, and an optional `context`.
"#" character, and an optional `context`.
Observation Markdown format:
@@ -300,73 +260,6 @@ Examples of relations:
- documented_in [[Coffee Journal]]
```
### Complete Example
Here's a complete example of a note with frontmatter, observations, and relations:
```markdown
---
title: Pour Over Coffee Method
type: note
permalink: pour-over-coffee-method
tags:
- brewing
- coffee
- techniques
---
# Pour Over Coffee Method
This note documents the pour over brewing method and my experiences with it.
## Overview
The pour over method involves pouring hot water through coffee grounds in a filter. The water drains through the coffee
and filter into a carafe or cup.
## Observations
- [equipment] Hario V60 dripper produces clean, bright cup #gear
- [technique] Pour in concentric circles to ensure even extraction
- [ratio] 1:16 coffee-to-water ratio works best for balanced flavor
- [timing] Total brew time should be 2:30-3:00 minutes for medium roast
- [temperature] Water at 205°F (96°C) extracts optimal flavor compounds
- [grind] Medium-fine grind similar to table salt texture
- [tip] 30-45 second bloom with double the coffee weight in water
- [result] Produces a cleaner cup with more distinct flavor notes than immersion methods
## Relations
- complements [[Light Roast Beans]]
- requires [[Gooseneck Kettle]]
- contrasts_with [[French Press Method]]
- pairs_with [[Breakfast Pastries]]
- documented_in [[Brewing Journal]]
- inspired_by [[Japanese Brewing Techniques]]
- affects [[Flavor Extraction]]
- part_of [[Morning Ritual]]
```
Basic Memory will parse the Markdown and derive the semantic relationships in the content. When you run
`basic-memory sync`:
1. New and changed files are detected
2. Markdown patterns become semantic knowledge:
- `[tech]` becomes a categorized observation
- `[[WikiLink]]` creates a relation in the knowledge graph
- Tags and metadata are indexed for search
3. A SQLite database maintains these relationships for fast querying
4. MCP-compatible LLMs can access this knowledge via memory:// URLs
This creates a two-way flow where:
- Humans write and edit Markdown files
- LLMs read and write through the MCP protocol
- Sync keeps everything consistent
- All knowledge stays in local files.
## Using with Claude Desktop
Basic Memory is built using the MCP (Model Context Protocol) and works with the Claude desktop app (https://claude.ai/):
@@ -440,263 +333,20 @@ canvas(nodes, edges, title, folder) - Generate knowledge visualizations
"What have I been working on in the past week?"
```
## Multiple Projects
## Futher info
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).
See the [Documentation](https://memory.basicmachines.co/) for more info, including:
### 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
## Design Philosophy
Basic Memory is built on some key ideas:
- Your knowledge should stay in files you control
- Both humans and AI should use natural formats
- Simple text patterns can capture rich meaning
- Local-first doesn't mean feature-poor
- Knowledge should persist across conversations
- AI assistants should build on past context
- File formats should be human-readable and editable
- Semantic structure should emerge from natural patterns
- Knowledge graphs should be both AI and human navigable
- Systems should augment human memory, not replace it
## Importing Existing Data
Basic Memory provides CLI commands to import data from various sources, converting them into the structured Markdown
format:
### Claude.ai
First, request an export of your data from your Claude account. The data will be emailed to you in several files,
including
`conversations.json` and `projects.json`.
Import Claude.ai conversation data
```bash
basic-memory import claude conversations
```
The conversations will be turned into Markdown files and placed in the "conversations" folder by default (this can be
changed with the --folder arg).
Example:
```bash
Importing chats from conversations.json...writing to .../basic-memory
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
╭────────────────────────────╮
│ Import complete! │
│ │
│ Imported 307 conversations │
│ Containing 7769 messages │
╰────────────────────────────╯
```
Next, you can run the `sync` command to import the data into basic-memory
```bash
basic-memory sync
```
You can also import project data from Claude.ai
```bash
➜ basic-memory import claude projects
Importing projects from projects.json...writing to .../basic-memory/projects
Reading project data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
╭────────────────────────────────╮
│ Import complete! │
│ │
│ Imported 101 project documents │
│ Imported 32 prompt templates │
╰────────────────────────────────╯
Run 'basic-memory sync' to index the new files.
```
### OpenAI ChatGPT
```bash
➜ basic-memory import chatgpt
Importing chats from conversations.json...writing to .../basic-memory/conversations
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
╭────────────────────────────╮
│ Import complete! │
│ │
│ Imported 198 conversations │
│ Containing 11777 messages │
╰────────────────────────────╯
```
### Knowledge Graph Memory Server
From the MCP Server: https://github.com/modelcontextprotocol/servers/tree/main/src/memory
```bash
➜ basic-memory import memory-json
Importing from memory.json...writing to .../basic-memory
Reading memory.json... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
Creating entities... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
╭──────────────────────╮
│ Import complete! │
│ │
│ Created 126 entities │
│ Added 252 relations │
╰──────────────────────╯
```
## Working with Your Knowledge Base
Once you've built up a knowledge base, you can interact with it in several ways:
### Command Line Interface
Basic Memory provides a powerful CLI for managing your knowledge:
```bash
# See all available commands
basic-memory --help
# Check the status of your knowledge sync
basic-memory status
# Access specific tool functionality directly
basic-memory tools
# Start a continuous sync process
basic-memory sync --watch
```
### Obsidian Integration
Basic Memory works seamlessly with [Obsidian](https://obsidian.md/), a popular knowledge management app:
1. Point Obsidian to your Basic Memory directory
2. Use standard Obsidian features like backlinks and graph view
3. See your knowledge graph visually
4. Use the canvas visualization generated by Basic Memory
### File Organization
Basic Memory is flexible about how you organize your files:
- Group by topic in folders
- Use a flat structure with descriptive filenames
- Add custom metadata in frontmatter
- Tag files for better searchability
The system will build the semantic knowledge graph regardless of your file organization preference.
## 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 tools write-note --title "Test Note" --folder "notes"
# Pipe output of a file into a new note
cat README.md | basic-memory tools write-note --title "Project README" --folder "documentation"
# Process text through other tools before saving as a note
cat data.txt | grep "important" | basic-memory tools 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 tools 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 tools write-note --title "Meeting Notes" --folder "meetings" < meeting_notes.md
```
- [Complete User Guide](https://memory.basicmachines.co/docs/user-guide)
- [CLI tools](https://memory.basicmachines.co/docs/cli-reference)
- [Managing multiple Projects](https://memory.basicmachines.co/docs/cli-reference#project)
- [Importing data from OpenAI/Claude Projects](https://memory.basicmachines.co/docs/cli-reference#import)
## License
AGPL-3.0
Contributions are welcome. See the [Contributing](CONTRIBUTING.md) guide for info about setting up the project locally
and submitting PRs.
Built with ♥️ by Basic Machines
+13
View File
@@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 0.x.x | :white_check_mark: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
If you find a vulnerability, please contact hello@basicmachines.co
-1
View File
@@ -1 +0,0 @@
{}
-1
View File
@@ -1 +0,0 @@
{}
-31
View File
@@ -1,31 +0,0 @@
{
"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
@@ -1,6 +0,0 @@
{
"siteId": "947ee055a8c6f1a57efa4afa09791e62",
"host": "publish-01.obsidian.md",
"included": [],
"excluded": []
}
-167
View File
@@ -1,167 +0,0 @@
{
"main": {
"id": "9361625269255fc5",
"type": "split",
"children": [
{
"id": "9220768da355d9a9",
"type": "tabs",
"children": [
{
"id": "cd58e5dfc86465e3",
"type": "leaf",
"state": {
"type": "empty",
"state": {},
"icon": "lucide-file",
"title": "New tab"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "932aa23fccfbb069",
"type": "split",
"children": [
{
"id": "c60acde475f2bc34",
"type": "tabs",
"children": [
{
"id": "a06f3217ff27ae41",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "Files"
}
},
{
"id": "c338fa22e7339c9a",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "Search"
}
},
{
"id": "56de0827023b2535",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "Bookmarks"
}
}
]
}
],
"direction": "horizontal",
"width": 300
},
"right": {
"id": "c3389d21c08cd71c",
"type": "split",
"children": [
{
"id": "11a1174f51a1cf86",
"type": "tabs",
"children": [
{
"id": "bf2a1fb608e63866",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "Backlinks"
}
},
{
"id": "6f08673b57226dc1",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Outgoing links"
}
},
{
"id": "e987a9643ca3ec81",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "Tags"
}
},
{
"id": "108a72a022d7e501",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline"
}
}
]
}
],
"direction": "horizontal",
"width": 300,
"collapsed": true
},
"left-ribbon": {
"hiddenItems": {
"publish:Publish changes...": false,
"switcher:Open quick switcher": false,
"graph:Open graph view": false,
"canvas:Create new canvas": false,
"daily-notes:Open today's daily note": false,
"templates:Insert template": false,
"command-palette:Open command palette": false
}
},
"active": "a06f3217ff27ae41",
"lastOpenFiles": []
}
+1 -1
View File
@@ -3,7 +3,7 @@ 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).
+105 -14
View File
@@ -6,31 +6,52 @@ 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.
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
# Install with uv (recommended).
uv tool 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. The `uvx` command mentioned in the Claude Desktop configuration is only for enabling Claude to access Basic Memory.
> **Important**: You need to install Basic Memory using one of the commands above to use the command line tools.
Using `uv tool install` will install the basic-memory package in a standalone virtual environment.
See the [UV docs](https://docs.astral.sh/uv/concepts/tools/) for more info.
### 2. Configure Claude Desktop
To enable Claude to read and write to your knowledge base, edit the Claude Desktop configuration file (usually at `~/Library/Application Support/Claude/claude_desktop_config.json`):
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": "uvx",
"command": "/absolute/path/to/uvx",
"args": [
"basic-memory",
"mcp"
@@ -40,7 +61,13 @@ To enable Claude to read and write to your knowledge base, edit the Claude Deskt
}
```
This configuration uses `uvx` to execute Basic Memory without requiring a full installation in Claude's environment.
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
@@ -56,23 +83,82 @@ 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 a conversation in Claude Desktop** about any topic:
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..."
```
2. **Have a natural conversation** about the topic
3. **Ask Claude to create a note**:
4. **Ask Claude to create a note**:
```
You: "Could you create a note summarizing what we've discussed about coffee brewing?"
```
4. **Claude creates a Markdown file** in your `~/basic-memory` directory
5. **Confirm note creation**:
Claude will confirm when the note has been created and where it's stored.
5. **View and edit the file** with any text editor or Obsidian
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
@@ -87,6 +173,7 @@ 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
@@ -100,6 +187,7 @@ 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
@@ -113,10 +201,13 @@ 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
@@ -173,4 +264,4 @@ After getting started, explore these areas:
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
5. **Review the [[CLI Reference]]** for command line tools
+145 -1
View File
@@ -62,6 +62,114 @@ Basic Memory is designed with privacy as a core principle:
## 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:
@@ -73,6 +181,42 @@ Basic Memory's core data model consists of:
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:
@@ -94,6 +238,6 @@ The search functionality:
5. Ranks results by relevance to the query
## Relations
- relates_to [[Introduction to Basic Memory]] (System overview)
- relates_to [[Welcome to Basic memory]] (Overview)
- relates_to [[CLI Reference]] (Command line tools)
- implements [[Knowledge Format]] (File structure and format)
+64 -26
View File
@@ -6,7 +6,8 @@ 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.
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
@@ -45,7 +46,8 @@ You can create files directly:
## 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.
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
@@ -56,6 +58,7 @@ 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
@@ -63,6 +66,7 @@ Behind the scenes:
- 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
@@ -76,12 +80,14 @@ 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
@@ -95,12 +101,14 @@ 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
@@ -116,9 +124,26 @@ Enter a topic
Give instructions
![[prompt3.png|500]]
Claude can build context from the supplied topic.
![[prompt4.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
@@ -142,7 +167,8 @@ 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.
This triggers a specialized search that returns precise results with document titles, relevant excerpts, and offers to
explore specific documents.
### Boolean Search
@@ -179,6 +205,7 @@ Basic Memory supports standard boolean operators:
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
@@ -193,6 +220,7 @@ 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
@@ -206,6 +234,7 @@ 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
@@ -213,6 +242,7 @@ This builds a complete picture by:
### 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
@@ -273,10 +303,12 @@ permalink: 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]]
```
@@ -300,11 +332,13 @@ 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)
@@ -426,16 +460,15 @@ basic-memory import claude conversations
basic-memory import chatgpt
```
## Multiple Projects
## 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).
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
### Managing Projects
```bash
# List all configured projects
basic-memory project list
@@ -452,11 +485,11 @@ 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:
### 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
@@ -464,20 +497,21 @@ 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:
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
### 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
@@ -493,6 +527,7 @@ Each project maintains:
### 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`
@@ -500,6 +535,7 @@ If changes aren't showing up:
### 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
@@ -507,11 +543,13 @@ If content isn't found:
### 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)
+10 -123
View File
@@ -14,8 +14,6 @@ Basic Memory is a knowledge management system that allows you to build a persist
with AI assistants. All knowledge is stored in standard Markdown files on your computer, giving you full control and
ownership of your data.
## Core Functions
Basic Memory connects you and AI assistants through shared knowledge:
1. **Captures knowledge** from natural conversations with AI assistants
@@ -26,132 +24,21 @@ Basic Memory connects you and AI assistants through shared knowledge:
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]]
## Pick up your conversation right where you left off
## Technical Architecture
- AI assistants can load context from local files in a new conversation
- Notes are saved locally as Markdown files in real time
- No project knowledge or special prompting required
![[Claude-Obsidian-Demo.mp4]]
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)
- **Memory:// URI scheme** - For precise knowledge referencing and navigation
- **Model Context Protocol (MCP)** - For seamless AI assistant integration
## Knowledge Structure
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",
"context": null,
"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",
"context": null,
"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.
## User Control and File Management
Basic Memory gives you complete control over your knowledge:
- **Local-first storage** - All knowledge lives on your computer
@@ -160,7 +47,7 @@ Basic Memory gives you complete control over your knowledge:
- **Version control ready** - Use git for history, branching, and collaboration
- **Edit anywhere** - Modify files with any text editor or Obsidian
Changes to files automatically update the knowledge graph, and AI assistants can see your edits in future conversations.
Changes to files automatically sync with the knowledge graph, and AI assistants can see your edits in conversations.
## Documentation Map
@@ -169,11 +56,11 @@ 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]]
- Reference for AI assistants using Basic Memory [[AI Assistant Guide]]
- Technical implementation details [[Technical Information]]
- Command line tool reference [[CLI Reference]]
- 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
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

+5
View File
@@ -0,0 +1,5 @@
var analyticsScript = document.createElement('script');
analyticsScript.defer = true;
analyticsScript.setAttribute('data-website-id', '8d51086e-5c67-401e-97b0-b24706a6d4f3');
analyticsScript.src = 'https://cloud.umami.is/script.js';
document.head.appendChild(analyticsScript);
Binary file not shown.
-22
View File
@@ -1,22 +0,0 @@
{
"collapse-filter": true,
"search": "",
"showTags": false,
"showAttachments": false,
"hideUnresolved": false,
"showOrphans": true,
"collapse-color-groups": true,
"colorGroups": [],
"collapse-display": true,
"showArrow": false,
"textFadeMultiplier": 0,
"nodeSizeMultiplier": 1,
"lineSizeMultiplier": 1,
"collapse-forces": true,
"centerStrength": 0.518713248970312,
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 1,
"close": false
}
-186
View File
@@ -1,186 +0,0 @@
{
"main": {
"id": "7ae5f8d0583c3a18",
"type": "split",
"children": [
{
"id": "22434516a5f1a19f",
"type": "tabs",
"children": [
{
"id": "6fae14c14e044a89",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Coffee Notes/Coffee Brewing Methods.md",
"mode": "source",
"source": true
},
"icon": "lucide-file",
"title": "Coffee Brewing Methods"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "402ef826b6e8c27a",
"type": "split",
"children": [
{
"id": "7df070b9023ec8de",
"type": "tabs",
"children": [
{
"id": "6fc235d3a84e414f",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "Files"
}
},
{
"id": "8679c962d1bfd321",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "Search"
}
},
{
"id": "8848cf5cc01540b2",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "Bookmarks"
}
}
]
}
],
"direction": "horizontal",
"width": 264.5
},
"right": {
"id": "5bfd609e7511200a",
"type": "split",
"children": [
{
"id": "5b83fb5c13b27ce5",
"type": "tabs",
"children": [
{
"id": "396b2dfa99a4a44e",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"file": "Untitled.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "Backlinks for Untitled"
}
},
{
"id": "b606de8f7f078b60",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"file": "Untitled.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Outgoing links from Untitled"
}
},
{
"id": "2788e9059ef83663",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "Tags"
}
},
{
"id": "cdf344bf83b2f251",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "Untitled.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline of Untitled"
}
}
]
}
],
"direction": "horizontal",
"width": 682.5,
"collapsed": true
},
"left-ribbon": {
"hiddenItems": {
"switcher:Open quick switcher": false,
"graph:Open graph view": false,
"canvas:Create new canvas": false,
"daily-notes:Open today's daily note": false,
"templates:Insert template": false,
"command-palette:Open command palette": false,
"publish:Publish changes...": false
}
},
"active": "6fae14c14e044a89",
"lastOpenFiles": [
"Untitled.md",
"Coffee Notes/Coffee Brewing Methods.md",
"Coffee Notes/Coffee Knowledge Base.md",
"Coffee Notes/Flavor Extraction.md",
"Coffee Notes/Coffee Flavor Map.md",
"Coffee Notes/Coffee Bean Origins.md",
"Coffee Notes/Perfect Pour Over Coffee Method.canvas",
"Coffee Notes/Coffee Brewing Methods Venn Diagram.canvas",
"Coffee Notes/Pour Over Techniques.md",
"Coffee Notes/Tasting Notes.md",
"Coffee Notes/Hybrid Pour Over Technique.md"
]
}
View File
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "basic-memory"
version = "0.9.0"
version = "0.10.0"
description = "Local-first knowledge management combining Zettelkasten with knowledge graphs"
readme = "README.md"
requires-python = ">=3.12.1"
+15
View File
@@ -0,0 +1,15 @@
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
properties: {}
description: No configuration required. This MCP server runs using the default command.
commandFunction: |-
(config) => ({
command: 'basic-memory',
args: ['mcp']
})
exampleConfig: {}
+1 -1
View File
@@ -1,3 +1,3 @@
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
__version__ = "0.9.0"
__version__ = "0.10.0"
@@ -3,9 +3,6 @@
import json
from datetime import datetime
from fastapi import APIRouter
from sqlalchemy import text
from basic_memory.config import config, config_manager
from basic_memory.deps import (
ProjectInfoRepositoryDep,
@@ -18,6 +15,8 @@ from basic_memory.schemas import (
SystemStatus,
)
from basic_memory.sync.watch_service import WATCH_STATUS_JSON
from fastapi import APIRouter
from sqlalchemy import text
router = APIRouter(prefix="/stats", tags=["statistics"])
@@ -262,7 +261,7 @@ async def get_system_status() -> SystemStatus:
watch_status_path = config.home / ".basic-memory" / WATCH_STATUS_JSON
if watch_status_path.exists():
try:
watch_status = json.loads(watch_status_path.read_text())
watch_status = json.loads(watch_status_path.read_text(encoding="utf-8"))
except Exception: # pragma: no cover
pass
@@ -7,15 +7,14 @@ from pathlib import Path
from typing import Dict, Any, List, Annotated, Set, Optional
import typer
from loguru import logger
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
from basic_memory.cli.app import import_app
from basic_memory.config import config
from basic_memory.markdown import EntityParser, MarkdownProcessor
from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter
from loguru import logger
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
console = Console()
@@ -167,7 +166,7 @@ async def process_chatgpt_json(
read_task = progress.add_task("Reading chat data...", total=None)
# Read conversations
conversations = json.loads(json_path.read_text())
conversations = json.loads(json_path.read_text(encoding="utf-8"))
progress.update(read_task, total=len(conversations))
# Process each conversation
@@ -7,15 +7,14 @@ from pathlib import Path
from typing import Dict, Any, List, Annotated
import typer
from loguru import logger
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
from basic_memory.cli.app import claude_app
from basic_memory.config import config
from basic_memory.markdown import EntityParser, MarkdownProcessor
from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter
from loguru import logger
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
console = Console()
@@ -124,7 +123,7 @@ async def process_conversations_json(
read_task = progress.add_task("Reading chat data...", total=None)
# Read chat data - handle array of arrays format
data = json.loads(json_path.read_text())
data = json.loads(json_path.read_text(encoding="utf-8"))
conversations = [chat for chat in data]
progress.update(read_task, total=len(conversations))
@@ -6,15 +6,14 @@ from pathlib import Path
from typing import Dict, Any, Annotated, Optional
import typer
from loguru import logger
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
from basic_memory.cli.app import claude_app
from basic_memory.config import config
from basic_memory.markdown import EntityParser, MarkdownProcessor
from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter
from loguru import logger
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
console = Console()
@@ -103,7 +102,7 @@ async def process_projects_json(
read_task = progress.add_task("Reading project data...", total=None)
# Read project data
data = json.loads(json_path.read_text())
data = json.loads(json_path.read_text(encoding="utf-8"))
progress.update(read_task, total=len(data))
# Track import counts
+3 -4
View File
@@ -5,13 +5,12 @@ import os
from pathlib import Path
from typing import Any, Dict, Literal, Optional
import basic_memory
from basic_memory.utils import setup_logging
from loguru import logger
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
import basic_memory
from basic_memory.utils import setup_logging
DATABASE_NAME = "memory.db"
DATA_DIR_NAME = ".basic-memory"
CONFIG_FILE_NAME = "config.json"
@@ -111,7 +110,7 @@ class ConfigManager:
"""Load configuration from file or create default."""
if self.config_file.exists():
try:
data = json.loads(self.config_file.read_text())
data = json.loads(self.config_file.read_text(encoding="utf-8"))
return BasicMemoryConfig(**data)
except Exception as e:
logger.error(f"Failed to load config: {e}")
+3 -3
View File
@@ -85,7 +85,7 @@ async def write_file_atomic(path: FilePath, content: str) -> None:
temp_path = path_obj.with_suffix(".tmp")
try:
temp_path.write_text(content)
temp_path.write_text(content, encoding="utf-8")
temp_path.replace(path_obj)
logger.debug("Wrote file atomically", path=str(path_obj), content_length=len(content))
except Exception as e: # pragma: no cover
@@ -203,7 +203,7 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
path_obj = Path(path) if isinstance(path, str) else path
# Read current content
content = path_obj.read_text()
content = path_obj.read_text(encoding="utf-8")
# Parse current frontmatter
current_fm = {}
@@ -215,7 +215,7 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
new_fm = {**current_fm, **updates}
# Write new file with updated frontmatter
yaml_fm = yaml.dump(new_fm, sort_keys=False)
yaml_fm = yaml.dump(new_fm, sort_keys=False, allow_unicode=True)
final_content = f"---\n{yaml_fm}---\n\n{content.strip()}"
logger.debug("Updating frontmatter", path=str(path_obj), update_keys=list(updates.keys()))
@@ -83,7 +83,7 @@ class MarkdownProcessor:
"""
# Dirty check if needed
if expected_checksum is not None:
current_content = path.read_text()
current_content = path.read_text(encoding="utf-8")
current_checksum = await file_utils.compute_checksum(current_content)
if current_checksum != expected_checksum:
raise DirtyFileError(f"File {path} has been modified")
@@ -1,8 +1,7 @@
from pathlib import Path
from loguru import logger
from basic_memory.mcp.server import mcp
from loguru import logger
@mcp.resource(
@@ -20,7 +19,9 @@ def ai_assistant_guide() -> str:
A focused guide on Basic Memory usage.
"""
logger.info("Loading AI assistant guide resource")
guide_doc = Path(__file__).parent.parent.parent.parent.parent / "static" / "ai_assistant_guide.md"
content = guide_doc.read_text()
guide_doc = (
Path(__file__).parent.parent.parent.parent.parent / "static" / "ai_assistant_guide.md"
)
content = guide_doc.read_text(encoding="utf-8")
logger.info(f"Loaded AI assistant guide ({len(content)} chars)")
return content
+3 -5
View File
@@ -5,8 +5,6 @@ from os import stat_result
from pathlib import Path
from typing import Any, Dict, Tuple, Union
from loguru import logger
from basic_memory import file_utils
from basic_memory.file_utils import FileError
from basic_memory.markdown.markdown_processor import MarkdownProcessor
@@ -14,6 +12,7 @@ from basic_memory.models import Entity as EntityModel
from basic_memory.schemas import Entity as EntitySchema
from basic_memory.services.exceptions import FileOperationError
from basic_memory.utils import FilePath
from loguru import logger
class FileService:
@@ -171,8 +170,7 @@ class FileService:
try:
logger.debug("Reading file", operation="read_file", path=str(full_path))
content = full_path.read_text()
content = full_path.read_text(encoding="utf-8")
checksum = await file_utils.compute_checksum(content)
logger.debug(
@@ -236,7 +234,7 @@ class FileService:
try:
if self.is_markdown(path):
# read str
content = full_path.read_text()
content = full_path.read_text(encoding="utf-8")
else:
# read bytes
content = full_path.read_bytes()
+23 -13
View File
@@ -5,16 +5,15 @@ from datetime import datetime
from pathlib import Path
from typing import List, Optional, Set
from basic_memory.config import ProjectConfig
from basic_memory.services.file_service import FileService
from basic_memory.sync.sync_service import SyncService
from loguru import logger
from pydantic import BaseModel
from rich.console import Console
from watchfiles import awatch
from watchfiles.main import FileChange, Change
from basic_memory.config import ProjectConfig
from basic_memory.services.file_service import FileService
from basic_memory.sync.sync_service import SyncService
WATCH_STATUS_JSON = "watch-status.json"
@@ -138,6 +137,10 @@ class WatchService:
if part.startswith("."):
return False
# Skip temp files used in atomic operations
if path.endswith(".tmp"):
return False
return True
async def write_status(self):
@@ -161,6 +164,11 @@ class WatchService:
for change, path in changes:
# convert to relative path
relative_path = str(Path(path).relative_to(directory))
# Skip .tmp files - they're temporary and shouldn't be synced
if relative_path.endswith(".tmp"):
continue
if change == Change.added:
adds.append(relative_path)
elif change == Change.deleted:
@@ -184,8 +192,8 @@ class WatchService:
# We don't need to process directories, only the files inside them
# This prevents errors when trying to compute checksums or read directories as files
added_full_path = directory / added_path
if added_full_path.is_dir():
logger.debug("Skipping directory for move detection", path=added_path)
if not added_full_path.exists() or added_full_path.is_dir():
logger.debug("Skipping non-existent or directory path", path=added_path)
processed.add(added_path)
continue
@@ -247,10 +255,12 @@ class WatchService:
if path not in processed:
# Skip directories - only process files
full_path = directory / path
if full_path.is_dir(): # pragma: no cover
logger.debug("Skipping directory", path=path)
processed.add(path)
continue
if not full_path.exists() or full_path.is_dir():
logger.debug(
"Skipping non-existent or directory path", path=path
) # pragma: no cover
processed.add(path) # pragma: no cover
continue # pragma: no cover
logger.debug("Processing new file", path=path)
entity, checksum = await self.sync_service.sync_file(path, new=True)
@@ -281,8 +291,8 @@ class WatchService:
if path not in processed:
# Skip directories - only process files
full_path = directory / path
if full_path.is_dir():
logger.debug("Skipping directory", path=path)
if not full_path.exists() or full_path.is_dir():
logger.debug("Skipping non-existent or directory path", path=path)
processed.add(path)
continue
@@ -341,4 +351,4 @@ class WatchService:
duration_ms=duration_ms,
)
await self.write_status()
await self.write_status()
+181
View File
@@ -0,0 +1,181 @@
"""Test proper handling of .tmp files during sync."""
import asyncio
from pathlib import Path
import pytest
from watchfiles import Change
from basic_memory.sync.watch_service import WatchService
async def create_test_file(path: Path, content: str = "test content") -> None:
"""Create a test file with given content."""
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content)
@pytest.mark.asyncio
async def test_temp_file_filter(watch_service):
"""Test that .tmp files are correctly filtered out."""
# Test filter_changes method directly
tmp_path = str(watch_service.config.home / "test.tmp")
assert not watch_service.filter_changes(Change.added, tmp_path)
# Test with valid file
valid_path = str(watch_service.config.home / "test.md")
assert watch_service.filter_changes(Change.added, valid_path)
@pytest.mark.asyncio
async def test_handle_tmp_files(watch_service, test_config, monkeypatch):
"""Test handling of .tmp files during sync process."""
project_dir = test_config.home
# Create a .tmp file - this simulates a file being written with write_file_atomic
tmp_file = project_dir / "test.tmp"
await create_test_file(tmp_file, "This is a temporary file")
# Create the target final file
final_file = project_dir / "test.md"
await create_test_file(final_file, "This is the final file")
# Setup changes that include both the .tmp and final file
changes = {
(Change.added, str(tmp_file)),
(Change.added, str(final_file)),
}
# Track sync_file calls
sync_calls = []
# Mock sync_file to track calls
original_sync_file = watch_service.sync_service.sync_file
async def mock_sync_file(path, new=True):
sync_calls.append(path)
return await original_sync_file(path, new)
monkeypatch.setattr(watch_service.sync_service, "sync_file", mock_sync_file)
# Handle changes
await watch_service.handle_changes(project_dir, changes)
# Verify .tmp file was not processed
assert "test.tmp" not in sync_calls
assert "test.md" in sync_calls
# Verify only the final file got an entity
tmp_entity = await watch_service.sync_service.entity_repository.get_by_file_path("test.tmp")
final_entity = await watch_service.sync_service.entity_repository.get_by_file_path("test.md")
assert tmp_entity is None, "Temp file should not have an entity"
assert final_entity is not None, "Final file should have an entity"
@pytest.mark.asyncio
async def test_atomic_write_tmp_file_handling(watch_service, test_config, monkeypatch):
"""Test handling of file changes during atomic write operations."""
project_dir = test_config.home
# This test simulates the full atomic write process:
# 1. First a .tmp file is created
# 2. Then the .tmp file is renamed to the final file
# 3. Both events are processed by the watch service
# Setup file paths
tmp_path = project_dir / "document.tmp"
final_path = project_dir / "document.md"
# Create mockup of the atomic write process
await create_test_file(tmp_path, "Content for document")
# First batch of changes - .tmp file created
changes1 = {(Change.added, str(tmp_path))}
# Process first batch
await watch_service.handle_changes(project_dir, changes1)
# Now "replace" the temp file with the final file
tmp_path.rename(final_path)
# Second batch of changes - .tmp file deleted, final file added
changes2 = {
(Change.deleted, str(tmp_path)),
(Change.added, str(final_path))
}
# Process second batch
await watch_service.handle_changes(project_dir, changes2)
# Verify only the final file is in the database
tmp_entity = await watch_service.sync_service.entity_repository.get_by_file_path("document.tmp")
final_entity = await watch_service.sync_service.entity_repository.get_by_file_path("document.md")
assert tmp_entity is None, "Temp file should not have an entity"
assert final_entity is not None, "Final file should have an entity"
# Check events
new_events = [e for e in watch_service.state.recent_events if e.action == "new"]
assert len(new_events) == 1
assert new_events[0].path == "document.md"
@pytest.mark.asyncio
async def test_rapid_atomic_writes(watch_service, test_config):
"""Test handling of rapid atomic writes to the same destination."""
project_dir = test_config.home
# This test simulates multiple rapid atomic writes to the same file:
# 1. Several .tmp files are created one after another
# 2. Each is then renamed to the same final file
# 3. Events are batched and processed together
# Setup file paths
tmp1_path = project_dir / "document.1.tmp"
tmp2_path = project_dir / "document.2.tmp"
final_path = project_dir / "document.md"
# Create multiple temp files that will be used in sequence
await create_test_file(tmp1_path, "First version")
await create_test_file(tmp2_path, "Second version")
# Simulate the first atomic write
tmp1_path.rename(final_path)
# Brief pause to ensure file system registers the change
await asyncio.sleep(0.1)
# Read content to verify
content1 = final_path.read_text()
assert content1 == "First version"
# Simulate the second atomic write
tmp2_path.rename(final_path)
# Verify content was updated
content2 = final_path.read_text()
assert content2 == "Second version"
# Create a batch of changes that might arrive in mixed order
changes = {
(Change.added, str(tmp1_path)),
(Change.deleted, str(tmp1_path)),
(Change.added, str(tmp2_path)),
(Change.deleted, str(tmp2_path)),
(Change.added, str(final_path)),
(Change.modified, str(final_path)),
}
# Process all changes
await watch_service.handle_changes(project_dir, changes)
# Verify only the final file is in the database
final_entity = await watch_service.sync_service.entity_repository.get_by_file_path("document.md")
assert final_entity is not None
# Also verify no tmp entities were created
tmp1_entity = await watch_service.sync_service.entity_repository.get_by_file_path("document.1.tmp")
tmp2_entity = await watch_service.sync_service.entity_repository.get_by_file_path("document.2.tmp")
assert tmp1_entity is None
assert tmp2_entity is None
Generated
+12 -11
View File
@@ -1,7 +1,8 @@
version = 1
revision = 1
requires-python = ">=3.12.1"
resolution-markers = [
"(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or (platform_machine == 'aarch64' and sys_platform != 'linux') or (platform_machine == 'armv7l' and sys_platform != 'linux') or (platform_machine == 'i686' and sys_platform != 'linux') or (platform_machine == 'ppc64le' and sys_platform != 'linux') or (platform_machine == 's390x' and sys_platform != 'linux') or (platform_machine == 'x86_64' and sys_platform != 'linux')",
"(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'",
"platform_machine == 'x86_64' and sys_platform == 'linux'",
"platform_machine == 'i686' and sys_platform == 'linux'",
"platform_machine == 'aarch64' and sys_platform == 'linux'",
@@ -52,7 +53,7 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
{ name = "typing-extensions", marker = "python_full_version < '3.13' and python_full_version >= '3.12.1'" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
wheels = [
@@ -70,7 +71,7 @@ wheels = [
[[package]]
name = "basic-memory"
version = "0.8.0"
version = "0.9.0"
source = { editable = "." }
dependencies = [
{ name = "aiosqlite" },
@@ -161,7 +162,7 @@ name = "cffi"
version = "1.17.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or (platform_machine == 'aarch64' and sys_platform != 'linux') or (platform_machine == 'armv7l' and sys_platform != 'linux') or (platform_machine == 'i686' and sys_platform != 'linux') or (platform_machine == 'ppc64le' and sys_platform != 'linux') or (platform_machine == 's390x' and sys_platform != 'linux') or (platform_machine == 'x86_64' and sys_platform != 'linux')" },
{ name = "pycparser", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
wheels = [
@@ -176,7 +177,7 @@ name = "click"
version = "8.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" },
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [
@@ -288,8 +289,8 @@ name = "dmgbuild"
version = "1.6.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ds-store", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or (platform_machine == 'aarch64' and sys_platform != 'linux') or (platform_machine == 'armv7l' and sys_platform != 'linux') or (platform_machine == 'i686' and sys_platform != 'linux') or (platform_machine == 'ppc64le' and sys_platform != 'linux') or (platform_machine == 's390x' and sys_platform != 'linux') or (platform_machine == 'x86_64' and sys_platform != 'linux')" },
{ name = "mac-alias", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or (platform_machine == 'aarch64' and sys_platform != 'linux') or (platform_machine == 'armv7l' and sys_platform != 'linux') or (platform_machine == 'i686' and sys_platform != 'linux') or (platform_machine == 'ppc64le' and sys_platform != 'linux') or (platform_machine == 's390x' and sys_platform != 'linux') or (platform_machine == 'x86_64' and sys_platform != 'linux')" },
{ name = "ds-store", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'" },
{ name = "mac-alias", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/17/89c089f4263b6c7f40a69355dd74f2cf5b5a58c4f7db79f76ed3d9e1ca5b/dmgbuild-1.6.4.tar.gz", hash = "sha256:adacada75ee517398d014e3fb250b004a2225f01e54832df52d38b9cd1530b74", size = 36824 }
wheels = [
@@ -310,7 +311,7 @@ name = "ds-store"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mac-alias", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or (platform_machine == 'aarch64' and sys_platform != 'linux') or (platform_machine == 'armv7l' and sys_platform != 'linux') or (platform_machine == 'i686' and sys_platform != 'linux') or (platform_machine == 'ppc64le' and sys_platform != 'linux') or (platform_machine == 's390x' and sys_platform != 'linux') or (platform_machine == 'x86_64' and sys_platform != 'linux')" },
{ name = "mac-alias", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7c/36/902259bf7ddb142dd91cf7a9794aa15e1a8ab985974f90375e5d3463b441/ds_store-1.3.1.tar.gz", hash = "sha256:c27d413caf13c19acb85d75da4752673f1f38267f9eb6ba81b3b5aa99c2d207c", size = 27052 }
wheels = [
@@ -797,7 +798,7 @@ email = [
{ name = "email-validator" },
]
timezone = [
{ name = "tzdata", marker = "platform_system == 'Windows' and python_full_version >= '3.12.1'" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
[[package]]
@@ -1191,7 +1192,7 @@ name = "sqlalchemy"
version = "2.0.38"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64' and python_full_version >= '3.12.1') or (python_full_version < '3.14' and platform_machine == 'WIN32' and python_full_version >= '3.12.1') or (python_full_version < '3.14' and platform_machine == 'aarch64' and python_full_version >= '3.12.1') or (python_full_version < '3.14' and platform_machine == 'amd64' and python_full_version >= '3.12.1') or (python_full_version < '3.14' and platform_machine == 'ppc64le' and python_full_version >= '3.12.1') or (python_full_version < '3.14' and platform_machine == 'win32' and python_full_version >= '3.12.1') or (python_full_version < '3.14' and platform_machine == 'x86_64' and python_full_version >= '3.12.1')" },
{ name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/08/9a90962ea72acd532bda71249a626344d855c4032603924b1b547694b837/sqlalchemy-2.0.38.tar.gz", hash = "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb", size = 9634782 }
@@ -1278,7 +1279,7 @@ name = "tzlocal"
version = "5.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tzdata", marker = "platform_system == 'Windows'" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/33/cc/11360404b20a6340b9b4ed39a3338c4af47bc63f87f6cea94dbcbde07029/tzlocal-5.3.tar.gz", hash = "sha256:2fafbfc07e9d8b49ade18f898d6bcd37ae88ce3ad6486842a2e4f03af68323d2", size = 30480 }
wheels = [