Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot] 65981874c3 chore: publish PR 923 image 2026-06-10 06:04:58 +00:00
304 changed files with 0 additions and 55175 deletions
-55
View File
@@ -1,55 +0,0 @@
# OAuth Configuration for Basic Memory MCP Server
# Copy this file to .env and update the values
# Enable OAuth authentication
FASTMCP_AUTH_ENABLED=true
# OAuth provider type: basic, github, google, or supabase
# - basic: Built-in OAuth provider with in-memory storage
# - github: Integrate with GitHub OAuth
# - google: Integrate with Google OAuth
# - supabase: Integrate with Supabase Auth (recommended for production)
FASTMCP_AUTH_PROVIDER=basic
# OAuth issuer URL (your MCP server URL)
FASTMCP_AUTH_ISSUER_URL=http://localhost:8000
# Documentation URL for OAuth endpoints
FASTMCP_AUTH_DOCS_URL=http://localhost:8000/docs/oauth
# Required scopes (comma-separated)
# Examples: read,write,admin
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
# Secret key for JWT tokens (auto-generated if not set)
# FASTMCP_AUTH_SECRET_KEY=your-secret-key-here
# Enable client registration endpoint
FASTMCP_AUTH_CLIENT_REGISTRATION_ENABLED=true
# Enable token revocation endpoint
FASTMCP_AUTH_REVOCATION_ENABLED=true
# Default scopes for new clients
FASTMCP_AUTH_DEFAULT_SCOPES=read
# Valid scopes that can be requested
FASTMCP_AUTH_VALID_SCOPES=read,write,admin
# Client secret expiry in seconds (optional)
# FASTMCP_AUTH_CLIENT_SECRET_EXPIRY=86400
# GitHub OAuth settings (if using github provider)
# GITHUB_CLIENT_ID=your-github-client-id
# GITHUB_CLIENT_SECRET=your-github-client-secret
# Google OAuth settings (if using google provider)
# GOOGLE_CLIENT_ID=your-google-client-id
# GOOGLE_CLIENT_SECRET=your-google-client-secret
# Supabase settings (if using supabase provider)
# SUPABASE_URL=https://your-project.supabase.co
# SUPABASE_ANON_KEY=your-anon-key
# SUPABASE_SERVICE_KEY=your-service-key # Optional, for admin operations
# SUPABASE_JWT_SECRET=your-jwt-secret # Optional, for token validation
# SUPABASE_ALLOWED_CLIENTS=client1,client2 # Comma-separated list of allowed client IDs
-38
View File
@@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve Basic Memory
title: '[BUG] '
labels: bug
assignees: ''
---
## Bug Description
A clear and concise description of what the bug is.
## Steps To Reproduce
Steps to reproduce the behavior:
1. Install version '...'
2. Run command '...'
3. Use tool/feature '...'
4. See error
## Expected Behavior
A clear and concise description of what you expected to happen.
## Actual Behavior
What actually happened, including error messages and output.
## Environment
- OS: [e.g. macOS 14.2, Ubuntu 22.04]
- Python version: [e.g. 3.12.1]
- Basic Memory version: [e.g. 0.1.0]
- Installation method: [e.g. pip, uv, source]
- Claude Desktop version (if applicable):
## Additional Context
- Configuration files (if relevant)
- Logs or screenshots
- Any special configuration or environment variables
## Possible Solution
If you have any ideas on what might be causing the issue or how to fix it, please share them here.
-8
View File
@@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Basic Memory Discussions
url: https://github.com/basicmachines-co/basic-memory/discussions
about: For questions, ideas, or more open-ended discussions
- name: Documentation
url: https://github.com/basicmachines-co/basic-memory#readme
about: Please check the documentation first before reporting an issue
-19
View File
@@ -1,19 +0,0 @@
---
name: Documentation improvement
about: Suggest improvements or report issues with documentation
title: '[DOCS] '
labels: documentation
assignees: ''
---
## Documentation Issue
Describe what's missing, unclear, or incorrect in the current documentation.
## Location
Where is the problematic documentation? (URL, file path, or section)
## Suggested Improvement
How would you improve this documentation? Please be as specific as possible.
## Additional Context
Any additional information or screenshots that might help explain the issue or improvement.
-28
View File
@@ -1,28 +0,0 @@
---
name: Feature request
about: Suggest an idea for Basic Memory
title: '[FEATURE] '
labels: enhancement
assignees: ''
---
## Feature Description
A clear and concise description of the feature you'd like to see implemented.
## Problem This Feature Solves
Describe the problem or limitation you're experiencing that this feature would address.
## Proposed Solution
Describe how you envision this feature working. Include:
- User workflow
- Interface design (if applicable)
- Technical approach (if you have ideas)
## Alternative Solutions
Have you considered any alternative solutions or workarounds?
## Additional Context
Add any other context, screenshots, or examples about the feature request here.
## Impact
How would this feature benefit you and other users of Basic Memory?
-12
View File
@@ -1,12 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
-81
View File
@@ -1,81 +0,0 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- name: Check organization membership
id: check_membership
uses: actions/github-script@v7
with:
script: |
let actor;
if (context.eventName === 'issue_comment') {
actor = context.payload.comment.user.login;
} else if (context.eventName === 'pull_request_review_comment') {
actor = context.payload.comment.user.login;
} else if (context.eventName === 'pull_request_review') {
actor = context.payload.review.user.login;
} else if (context.eventName === 'issues') {
actor = context.payload.issue.user.login;
}
console.log(`Checking membership for user: ${actor}`);
try {
const membership = await github.rest.orgs.getMembershipForUser({
org: 'basicmachines-co',
username: actor
});
console.log(`Membership status: ${membership.data.state}`);
// Allow if user is a member (public or private) or admin
const allowed = membership.data.state === 'active' &&
(membership.data.role === 'member' || membership.data.role === 'admin');
core.setOutput('is_member', allowed);
if (!allowed) {
core.notice(`User ${actor} is not a member of basicmachines-co organization`);
}
} catch (error) {
console.log(`Error checking membership: ${error.message}`);
core.setOutput('is_member', false);
core.notice(`User ${actor} is not a member of basicmachines-co organization`);
}
- name: Checkout repository
if: steps.check_membership.outputs.is_member == 'true'
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code
if: steps.check_membership.outputs.is_member == 'true'
id: claude
uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
allowed_tools: Bash(uv run pytest),Bash(uv run ruff check . --fix),Bash(uv run ruff format .),Bash(uv run pyright),Bash(make test),Bash(make lint),Bash(make format),Bash(make type-check),Bash(make check),Read,Write,Edit,MultiEdit,Glob,Grep,LS
-53
View File
@@ -1,53 +0,0 @@
name: Dev Release
on:
push:
branches: [main]
workflow_dispatch: # Allow manual triggering
jobs:
dev-release:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv
run: |
pip install uv
- name: Install dependencies and build
run: |
uv venv
uv sync
uv build
- name: Check if this is a dev version
id: check_version
run: |
VERSION=$(uv run python -c "import basic_memory; print(basic_memory.__version__)")
echo "version=$VERSION" >> $GITHUB_OUTPUT
if [[ "$VERSION" == *"dev"* ]]; then
echo "is_dev=true" >> $GITHUB_OUTPUT
echo "Dev version detected: $VERSION"
else
echo "is_dev=false" >> $GITHUB_OUTPUT
echo "Release version detected: $VERSION, skipping dev release"
fi
- name: Publish dev version to PyPI
if: steps.check_version.outputs.is_dev == 'true'
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_TOKEN }}
skip-existing: true # Don't fail if version already exists
-41
View File
@@ -1,41 +0,0 @@
name: "Pull Request Title"
on:
pull_request:
types:
- opened
- edited
- synchronize
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Configure allowed types based on what we want in our changelog
types: |
feat
fix
chore
docs
style
refactor
perf
test
build
ci
# Require at least one from scope list (optional)
scopes: |
core
cli
api
mcp
sync
ui
deps
installer
# Allow breaking changes (needs "!" after type/scope)
requireScopeForBreakingChange: true
-60
View File
@@ -1,60 +0,0 @@
name: Release
on:
push:
tags:
- 'v*' # Trigger on version tags like v1.0.0, v0.13.0, etc.
jobs:
release:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv
run: |
pip install uv
- name: Install dependencies and build
run: |
uv venv
uv sync
uv build
- name: Verify version matches tag
run: |
# Get version from built package
PACKAGE_VERSION=$(uv run python -c "import basic_memory; print(basic_memory.__version__)")
TAG_VERSION=${GITHUB_REF_NAME#v} # Remove 'v' prefix from tag
echo "Package version: $PACKAGE_VERSION"
echo "Tag version: $TAG_VERSION"
if [ "$PACKAGE_VERSION" != "$TAG_VERSION" ]; then
echo "Version mismatch! Package: $PACKAGE_VERSION, Tag: $TAG_VERSION"
exit 1
fi
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
dist/*.whl
dist/*.tar.gz
generate_release_notes: true
tag_name: ${{ github.ref_name }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_TOKEN }}
-53
View File
@@ -1,53 +0,0 @@
name: Tests
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# pull_request_target runs on the BASE of the PR, not the merge result.
# It has write permissions and access to secrets.
# It's useful for PRs from forks or automated PRs but requires careful use for security reasons.
# See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
pull_request_target:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ "3.12" ]
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install uv
run: |
pip install uv
- name: Create virtual env
run: |
uv venv
- name: Install dependencies
run: |
uv pip install -e .[dev]
- name: Run type checks
run: |
uv run make type-check
- name: Run tests
run: |
uv pip install pytest pytest-cov
uv run make test
-55
View File
@@ -1,55 +0,0 @@
*.py[cod]
__pycache__/
.pytest_cache/
.coverage
htmlcov/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Installer artifacts
installer/build/
installer/dist/
rw.*.dmg # Temporary disk images
# Virtual environments
.env
.venv
env/
venv/
ENV/
# IDE
.idea/
.vscode/
*.swp
*.swo
# macOS
.DS_Store
/.coverage.*
# obsidian docs:
/docs/.obsidian/
/examples/.obsidian/
/examples/.basic-memory/
# claude action
claude-output
**/.claude/settings.local.json
-14
View File
@@ -1,14 +0,0 @@
{
"mcpServers": {
"basic-memory": {
"command": "uv",
"args": [
"--directory",
"/Users/phernandez/dev/basicmachines/basic-memory",
"run",
"src/basic_memory/cli/main.py",
"mcp"
]
}
}
}
-1
View File
@@ -1 +0,0 @@
3.12
-42
View File
@@ -1,42 +0,0 @@
# OAuth Quick Start
Basic Memory supports OAuth authentication for secure access control. For detailed documentation, see [OAuth Authentication Guide](docs/OAuth%20Authentication%20Guide.md).
## Quick Test with MCP Inspector
```bash
# 1. Set a consistent secret key
export FASTMCP_AUTH_SECRET_KEY="test-secret-key"
# 2. Start server with OAuth
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
# 3. In another terminal, get a test token
export FASTMCP_AUTH_SECRET_KEY="test-secret-key" # Same key!
basic-memory auth test-auth
# 4. Copy the access token and use in MCP Inspector:
# - Server URL: http://localhost:8000/mcp
# - Transport: streamable-http
# - Custom Headers:
# Authorization: Bearer YOUR_ACCESS_TOKEN
# Accept: application/json, text/event-stream
```
## OAuth Endpoints
- `GET /authorize` - Authorization endpoint
- `POST /token` - Token exchange endpoint
- `GET /.well-known/oauth-authorization-server` - OAuth metadata
## Common Issues
1. **401 Unauthorized**: Make sure you're using the same secret key for both server and client
2. **404 Not Found**: Use `/authorize` not `/auth/authorize`
3. **Token Invalid**: Tokens don't persist across server restarts with basic provider
## Documentation
- [OAuth Authentication Guide](docs/OAuth%20Authentication%20Guide.md) - Complete setup guide
- [Supabase OAuth Setup](docs/Supabase%20OAuth%20Setup.md) - Production deployment
- [External OAuth Providers](docs/External%20OAuth%20Providers.md) - GitHub, Google integration
-864
View File
@@ -1,864 +0,0 @@
# CHANGELOG
## v0.13.0 (2025-06-03)
### Features
- **Multi-Project Management System** - Switch between projects instantly during conversations
([`993e88a`](https://github.com/basicmachines-co/basic-memory/commit/993e88a))
- Instant project switching with session context
- Project-specific operations and isolation
- Project discovery and management tools
- **Advanced Note Editing** - Incremental editing with append, prepend, find/replace, and section operations
([`6fc3904`](https://github.com/basicmachines-co/basic-memory/commit/6fc3904))
- `edit_note` tool with multiple operation types
- Smart frontmatter-aware editing
- Validation and error handling
- **Smart File Management** - Move notes with database consistency and search reindexing
([`9fb931c`](https://github.com/basicmachines-co/basic-memory/commit/9fb931c))
- `move_note` tool with rollback protection
- Automatic folder creation and permalink updates
- Full database consistency maintenance
- **Enhanced Search Capabilities** - Frontmatter tags now searchable, improved content discovery
([`3f5368e`](https://github.com/basicmachines-co/basic-memory/commit/3f5368e))
- YAML frontmatter tag indexing
- Improved FTS5 search functionality
- Project-scoped search operations
- **Production Features** - OAuth authentication, development builds, comprehensive testing
([`5f8d945`](https://github.com/basicmachines-co/basic-memory/commit/5f8d945))
- Development build automation
- MCP integration testing framework
- Enhanced CI/CD pipeline
### Bug Fixes
- **#118**: Fix YAML tag formatting to follow standard specification
([`2dc7e27`](https://github.com/basicmachines-co/basic-memory/commit/2dc7e27))
- **#110**: Make --project flag work consistently across CLI commands
([`02dd91a`](https://github.com/basicmachines-co/basic-memory/commit/02dd91a))
- **#93**: Respect custom permalinks in frontmatter for write_note
([`6b6fd76`](https://github.com/basicmachines-co/basic-memory/commit/6b6fd76))
- Fix list_directory path display to not include leading slash
([`6057126`](https://github.com/basicmachines-co/basic-memory/commit/6057126))
### Technical Improvements
- **Unified Database Architecture** - Single app-level database for better performance
- Migration from per-project databases to unified structure
- Project isolation with foreign key relationships
- Optimized queries and reduced file I/O
- **Comprehensive Testing** - 100% test coverage with integration testing
([`468a22f`](https://github.com/basicmachines-co/basic-memory/commit/468a22f))
- MCP integration test suite
- End-to-end testing framework
- Performance and edge case validation
### Documentation
- Add comprehensive testing documentation (TESTING.md)
- Update project management guides (PROJECT_MANAGEMENT.md)
- Enhanced note editing documentation (EDIT_NOTE.md)
- Updated release workflow documentation
### Breaking Changes
- **Database Migration**: Automatic migration from per-project to unified database.
Data will be re-index from the filesystem, resulting in no data loss.
- **Configuration Changes**: Projects now synced between config.json and database
- **Full Backward Compatibility**: All existing setups continue to work seamlessly
## v0.12.3 (2025-04-17)
### Bug Fixes
- Add extra logic for permalink generation with mixed Latin unicode and Chinese characters
([`73ea91f`](https://github.com/basicmachines-co/basic-memory/commit/73ea91fe0d1f7ab89b99a1b691d59fe608b7fcbb))
Signed-off-by: phernandez <paul@basicmachines.co>
- Modify recent_activity args to be strings instead of enums
([`3c1cc34`](https://github.com/basicmachines-co/basic-memory/commit/3c1cc346df519e703fae6412d43a92c7232c6226))
Signed-off-by: phernandez <paul@basicmachines.co>
## v0.12.2 (2025-04-08)
### Bug Fixes
- Utf8 for all file reads/write/open instead of default platform encoding
([#91](https://github.com/basicmachines-co/basic-memory/pull/91),
[`2934176`](https://github.com/basicmachines-co/basic-memory/commit/29341763318408ea8f1e954a41046c4185f836c6))
Signed-off-by: phernandez <paul@basicmachines.co>
## v0.12.1 (2025-04-07)
### Bug Fixes
- Run migrations and sync when starting mcp
([#88](https://github.com/basicmachines-co/basic-memory/pull/88),
[`78a3412`](https://github.com/basicmachines-co/basic-memory/commit/78a3412bcff83b46e78e26f8b9fce42ed9e05991))
## v0.12.0 (2025-04-06)
### Bug Fixes
- [bug] `#` character accumulation in markdown frontmatter tags prop
([#79](https://github.com/basicmachines-co/basic-memory/pull/79),
[`6c19c9e`](https://github.com/basicmachines-co/basic-memory/commit/6c19c9edf5131054ba201a109b37f15c83ef150c))
- [bug] Cursor has errors calling search tool
([#78](https://github.com/basicmachines-co/basic-memory/pull/78),
[`9d581ce`](https://github.com/basicmachines-co/basic-memory/commit/9d581cee133f9dde4a0a85118868227390c84161))
- [bug] Some notes never exit "modified" status
([#77](https://github.com/basicmachines-co/basic-memory/pull/77),
[`7930ddb`](https://github.com/basicmachines-co/basic-memory/commit/7930ddb2919057be30ceac8c4c19da6aaa1d3e92))
- [bug] write_note Tool Fails to Update Existing Files in Some Situations.
([#80](https://github.com/basicmachines-co/basic-memory/pull/80),
[`9bff1f7`](https://github.com/basicmachines-co/basic-memory/commit/9bff1f732e71bc60f88b5c2ce3db5a2aa60b8e28))
- Set default mcp log level to ERROR
([#81](https://github.com/basicmachines-co/basic-memory/pull/81),
[`248214c`](https://github.com/basicmachines-co/basic-memory/commit/248214cb114a269ca60ff6398e382f9e2495ad8e))
- Write_note preserves frontmatter fields in content
([#84](https://github.com/basicmachines-co/basic-memory/pull/84),
[`3f4d9e4`](https://github.com/basicmachines-co/basic-memory/commit/3f4d9e4d872ebc0ed719c61b24d803c14a9db5e6))
### Documentation
- Add VS Code instructions to README
([#76](https://github.com/basicmachines-co/basic-memory/pull/76),
[`43cbb7b`](https://github.com/basicmachines-co/basic-memory/commit/43cbb7b38cc0482ac0a41b6759320e3588186e43))
- Updated basicmachines.co links to be https
([#69](https://github.com/basicmachines-co/basic-memory/pull/69),
[`40ea28b`](https://github.com/basicmachines-co/basic-memory/commit/40ea28b0bfc60012924a69ecb76511daa4c7d133))
### Features
- Add watch to mcp process ([#83](https://github.com/basicmachines-co/basic-memory/pull/83),
[`00c8633`](https://github.com/basicmachines-co/basic-memory/commit/00c8633cfcee75ff640ff8fe81dafeb956281a94))
- Permalink enhancements ([#82](https://github.com/basicmachines-co/basic-memory/pull/82),
[`617e60b`](https://github.com/basicmachines-co/basic-memory/commit/617e60bda4a590678a5f551f10a73e7b47e3b13e))
- Avoiding "useless permalink values" for files without metadata - Enable permalinks to be updated
on move via config setting
## v0.11.0 (2025-03-29)
### Bug Fixes
- Just delete db for reset db instead of using migrations.
([#65](https://github.com/basicmachines-co/basic-memory/pull/65),
[`0743ade`](https://github.com/basicmachines-co/basic-memory/commit/0743ade5fc07440f95ecfd816ba7e4cfd74bca12))
Signed-off-by: phernandez <paul@basicmachines.co>
- Make logs for each process - mcp, sync, cli
([#64](https://github.com/basicmachines-co/basic-memory/pull/64),
[`f1c9570`](https://github.com/basicmachines-co/basic-memory/commit/f1c95709cbffb1b88292547b0b8f29fcca22d186))
Signed-off-by: phernandez <paul@basicmachines.co>
### Documentation
- Update broken "Multiple Projects" link in README.md
([#55](https://github.com/basicmachines-co/basic-memory/pull/55),
[`3c68b7d`](https://github.com/basicmachines-co/basic-memory/commit/3c68b7d5dd689322205c67637dca7d188111ee6b))
### Features
- Add bm command alias for basic-memory
([#67](https://github.com/basicmachines-co/basic-memory/pull/67),
[`069c0a2`](https://github.com/basicmachines-co/basic-memory/commit/069c0a21c630784e1bf47d2b7de5d6d1f6fadd7a))
Signed-off-by: phernandez <paul@basicmachines.co>
- Rename search tool to search_notes
([#66](https://github.com/basicmachines-co/basic-memory/pull/66),
[`b278276`](https://github.com/basicmachines-co/basic-memory/commit/b27827671dc010be3e261b8b221aca6b7f836661))
Signed-off-by: phernandez <paul@basicmachines.co>
## v0.10.1 (2025-03-25)
### Bug Fixes
- Make set_default_project also activate project for current session to fix #37
([`cbe72be`](https://github.com/basicmachines-co/basic-memory/commit/cbe72be10a646c0b03931bb39aff9285feae47f9))
This change makes the 'basic-memory project default <name>' command both: 1. Set the default project
for future invocations (persistent change) 2. Activate the project for the current session
(immediate change)
Added tests to verify this behavior, which resolves issue #37 where the project name and path
weren't changing properly when the default project was changed.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Make set_default_project also activate project for current session to fix #37
([`46c4fd2`](https://github.com/basicmachines-co/basic-memory/commit/46c4fd21645b109af59eb2a0201c7bd849b34a49))
This change makes the 'basic-memory project default <name>' command both: 1. Set the default project
for future invocations (persistent change) 2. Activate the project for the current session
(immediate change)
Added tests to verify this behavior, which resolves issue #37 where the project name and path
weren't changing properly when the default project was changed.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
- Move ai_assistant_guide.md into package resources to fix #39
([`390ff9d`](https://github.com/basicmachines-co/basic-memory/commit/390ff9d31ccee85bef732e8140b5eeecd7ee176f))
This change relocates the AI assistant guide from the static directory into the package resources
directory, ensuring it gets properly included in the distribution package and is accessible when
installed via pip/uv.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move ai_assistant_guide.md into package resources to fix #39
([`cc2cae7`](https://github.com/basicmachines-co/basic-memory/commit/cc2cae72c14b380f78ffeb67c2261e4dbee45faf))
This change relocates the AI assistant guide from the static directory into the package resources
directory, ensuring it gets properly included in the distribution package and is accessible when
installed via pip/uv.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
- Preserve custom frontmatter fields when updating notes
([`78f234b`](https://github.com/basicmachines-co/basic-memory/commit/78f234b1806b578a0a833e8ee4184015b7369a97))
Fixes #36 by modifying entity_service.update_entity() to read existing frontmatter from files before
updating them. Custom metadata fields such as Status, Priority, and Version are now preserved when
notes are updated through the write_note MCP tool.
Added test case that verifies this behavior by creating a note with custom frontmatter and then
updating it.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Preserve custom frontmatter fields when updating notes
([`e716946`](https://github.com/basicmachines-co/basic-memory/commit/e716946b4408d017eca4be720956d5a210b4e6b1))
Fixes #36 by modifying entity_service.update_entity() to read existing frontmatter from files before
updating them. Custom metadata fields such as Status, Priority, and Version are now preserved when
notes are updated through the write_note MCP tool.
Added test case that verifies this behavior by creating a note with custom frontmatter and then
updating it.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
### Chores
- Remove duplicate code in entity_service.py from bad merge
([`681af5d`](https://github.com/basicmachines-co/basic-memory/commit/681af5d4505dadc40b4086630f739d76bac9201d))
Signed-off-by: phernandez <paul@basicmachines.co>
### Documentation
- Add help docs to mcp cli tools
([`731b502`](https://github.com/basicmachines-co/basic-memory/commit/731b502d36cec253d114403d73b48fab3c47786e))
Signed-off-by: phernandez <paul@basicmachines.co>
- Add mcp badge, update cli reference, llms-install.md
([`b26afa9`](https://github.com/basicmachines-co/basic-memory/commit/b26afa927f98021246cd8b64858e57333595ea90))
Signed-off-by: phernandez <paul@basicmachines.co>
- Update CLAUDE.md ([#33](https://github.com/basicmachines-co/basic-memory/pull/33),
[`dfaf0fe`](https://github.com/basicmachines-co/basic-memory/commit/dfaf0fea9cf5b97d169d51a6276ec70162c21a7e))
fix spelling in CLAUDE.md: enviroment -> environment Signed-off-by: Ikko Eltociear Ashimine
<eltociear@gmail.com>
### Refactoring
- Move project stats into projct subcommand
([`2a881b1`](https://github.com/basicmachines-co/basic-memory/commit/2a881b1425c73947f037fbe7ac5539c015b62526))
Signed-off-by: phernandez <paul@basicmachines.co>
## 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
- Pre beta prep ([#20](https://github.com/basicmachines-co/basic-memory/pull/20),
[`6a4bd54`](https://github.com/basicmachines-co/basic-memory/commit/6a4bd546466a45107007b5000276b6c9bb62ef27))
fix: drop search_index table on db reindex
fix: ai_resource_guide.md path
chore: remove logfire
Signed-off-by: phernandez <paul@basicmachines.co>
### Documentation
- Update README.md and CLAUDE.md
([`182ec78`](https://github.com/basicmachines-co/basic-memory/commit/182ec7835567fc246798d9b4ad121b2f85bc6ade))
### Features
- Add project_info tool ([#19](https://github.com/basicmachines-co/basic-memory/pull/19),
[`d2bd75a`](https://github.com/basicmachines-co/basic-memory/commit/d2bd75a949cc4323cb376ac2f6cb39f47c78c428))
Signed-off-by: phernandez <paul@basicmachines.co>
- Beta work ([#17](https://github.com/basicmachines-co/basic-memory/pull/17),
[`e6496df`](https://github.com/basicmachines-co/basic-memory/commit/e6496df595f3cafde6cc836384ee8c60886057a5))
feat: Add multiple projects support
feat: enhanced read_note for when initial result is not found
fix: merge frontmatter when updating note
fix: handle directory removed on sync watch
- Implement boolean search ([#18](https://github.com/basicmachines-co/basic-memory/pull/18),
[`90d5754`](https://github.com/basicmachines-co/basic-memory/commit/90d5754180beaf4acd4be38f2438712555640b49))
## v0.8.0 (2025-02-28)
### Chores
- Formatting
([`93cc637`](https://github.com/basicmachines-co/basic-memory/commit/93cc6379ebb9ecc6a1652feeeecbf47fc992d478))
- Refactor logging setup
([`f4b703e`](https://github.com/basicmachines-co/basic-memory/commit/f4b703e57f0ddf686de6840ff346b8be2be499ad))
### Features
- Add enhanced prompts and resources
([#15](https://github.com/basicmachines-co/basic-memory/pull/15),
[`093dab5`](https://github.com/basicmachines-co/basic-memory/commit/093dab5f03cf7b090a9f4003c55507859bf355b0))
## Summary - Add comprehensive documentation to all MCP prompt modules - Enhance search prompt with
detailed contextual output formatting - Implement consistent logging and docstring patterns across
prompt utilities - Fix type checking in prompt modules
## Prompts Added/Enhanced - `search.py`: New formatted output with relevance scores, excerpts, and
next steps - `recent_activity.py`: Enhanced with better metadata handling and documentation -
`continue_conversation.py`: Improved context management
## Resources Added/Enhanced - `ai_assistant_guide`: Resource with description to give to LLM to
understand how to use the tools
## Technical improvements - Added detailed docstrings to all prompt modules explaining their purpose
and usage - Enhanced the search prompt with rich contextual output that helps LLMs understand
results - Created a consistent pattern for formatting output across prompts - Improved error
handling in metadata extraction - Standardized import organization and naming conventions - Fixed
various type checking issues across the codebase
This PR is part of our ongoing effort to improve the MCP's interaction quality with LLMs, making the
system more helpful and intuitive for AI assistants to navigate knowledge bases.
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: phernandez <phernandez@basicmachines.co>
- Add new `canvas` tool to create json canvas files in obsidian.
([#14](https://github.com/basicmachines-co/basic-memory/pull/14),
[`0d7b0b3`](https://github.com/basicmachines-co/basic-memory/commit/0d7b0b3d7ede7555450ddc9728951d4b1edbbb80))
Add new `canvas` tool to create json canvas files in obsidian.
---------
Co-authored-by: phernandez <phernandez@basicmachines.co>
- Incremental sync on watch ([#13](https://github.com/basicmachines-co/basic-memory/pull/13),
[`37a01b8`](https://github.com/basicmachines-co/basic-memory/commit/37a01b806d0758029d34a862e76d44c7e5d538a5))
- incremental sync on watch - sync non-markdown files in knowledge base - experimental
`read_resource` tool for reading non-markdown files in raw form (pdf, image)
## v0.7.0 (2025-02-19)
### Bug Fixes
- Add logfire instrumentation to tools
([`3e8e3e8`](https://github.com/basicmachines-co/basic-memory/commit/3e8e3e8961eae2e82839746e28963191b0aef0a0))
- Add logfire spans to cli
([`00d23a5`](https://github.com/basicmachines-co/basic-memory/commit/00d23a5ee15ddac4ea45e702dcd02ab9f0509276))
- Add logfire spans to cli
([`812136c`](https://github.com/basicmachines-co/basic-memory/commit/812136c8c22ad191d14ff32dcad91aae076d4120))
- Search query pagination params
([`bc9ca07`](https://github.com/basicmachines-co/basic-memory/commit/bc9ca0744ffe4296d7d597b4dd9b7c73c2d63f3f))
### Chores
- Fix tests
([`57984aa`](https://github.com/basicmachines-co/basic-memory/commit/57984aa912625dcde7877afb96d874c164af2896))
- Remove unused tests
([`2c8ed17`](https://github.com/basicmachines-co/basic-memory/commit/2c8ed1737d6769fe1ef5c96f8a2bd75b9899316a))
### Features
- Add cli commands for mcp tools
([`f5a7541`](https://github.com/basicmachines-co/basic-memory/commit/f5a7541da17e97403b7a702720a05710f68b223a))
- Add pagination to build_context and recent_activity
([`0123544`](https://github.com/basicmachines-co/basic-memory/commit/0123544556513af943d399d70b849b142b834b15))
- Add pagination to read_notes
([`02f8e86`](https://github.com/basicmachines-co/basic-memory/commit/02f8e866923d5793d2620076c709c920d99f2c4f))
## v0.6.0 (2025-02-18)
### Chores
- Re-add sync status console on watch
([`66b57e6`](https://github.com/basicmachines-co/basic-memory/commit/66b57e682f2e9c432bffd4af293b0d1db1d3469b))
### Features
- Configure logfire telemetry ([#12](https://github.com/basicmachines-co/basic-memory/pull/12),
[`6da1438`](https://github.com/basicmachines-co/basic-memory/commit/6da143898bd45cdab8db95b5f2b75810fbb741ba))
Co-authored-by: phernandez <phernandez@basicmachines.co>
## v0.5.0 (2025-02-18)
### Features
- Return semantic info in markdown after write_note
([#11](https://github.com/basicmachines-co/basic-memory/pull/11),
[`0689e7a`](https://github.com/basicmachines-co/basic-memory/commit/0689e7a730497827bf4e16156ae402ddc5949077))
Co-authored-by: phernandez <phernandez@basicmachines.co>
## v0.4.3 (2025-02-18)
### Bug Fixes
- Re do enhanced read note format ([#10](https://github.com/basicmachines-co/basic-memory/pull/10),
[`39bd5ca`](https://github.com/basicmachines-co/basic-memory/commit/39bd5ca08fd057220b95a8b5d82c5e73a1f5722b))
Co-authored-by: phernandez <phernandez@basicmachines.co>
## v0.4.2 (2025-02-17)
## v0.4.1 (2025-02-17)
### Bug Fixes
- Fix alemic config
([`71de8ac`](https://github.com/basicmachines-co/basic-memory/commit/71de8acfd0902fc60f27deb3638236a3875787ab))
- More alembic fixes
([`30cd74e`](https://github.com/basicmachines-co/basic-memory/commit/30cd74ec95c04eaa92b41b9815431f5fbdb46ef8))
## v0.4.0 (2025-02-16)
### Features
- Import chatgpt conversation data ([#9](https://github.com/basicmachines-co/basic-memory/pull/9),
[`56f47d6`](https://github.com/basicmachines-co/basic-memory/commit/56f47d6812982437f207629e6ac9a82e0e56514e))
Co-authored-by: phernandez <phernandez@basicmachines.co>
- Import claude.ai data ([#8](https://github.com/basicmachines-co/basic-memory/pull/8),
[`a15c346`](https://github.com/basicmachines-co/basic-memory/commit/a15c346d5ebd44344b76bad877bb4d1073fcbc3b))
Import Claude.ai conversation and project data to basic-memory Markdown format.
---------
Co-authored-by: phernandez <phernandez@basicmachines.co>
## v0.3.0 (2025-02-15)
### Bug Fixes
- Refactor db schema migrate handling
([`ca632be`](https://github.com/basicmachines-co/basic-memory/commit/ca632beb6fed5881f4d8ba5ce698bb5bc681e6aa))
## v0.2.21 (2025-02-15)
### Bug Fixes
- Fix osx installer github action
([`65ebe5d`](https://github.com/basicmachines-co/basic-memory/commit/65ebe5d19491e5ff047c459d799498ad5dd9cd1a))
- Handle memory:// url format in read_note tool
([`e080373`](https://github.com/basicmachines-co/basic-memory/commit/e0803734e69eeb6c6d7432eea323c7a264cb8347))
- Remove create schema from init_db
([`674dd1f`](https://github.com/basicmachines-co/basic-memory/commit/674dd1fd47be9e60ac17508476c62254991df288))
### Features
- Set version in var, output version at startup
([`a91da13`](https://github.com/basicmachines-co/basic-memory/commit/a91da1396710e62587df1284da00137d156fc05e))
## v0.2.20 (2025-02-14)
### Bug Fixes
- Fix installer artifact
([`8de84c0`](https://github.com/basicmachines-co/basic-memory/commit/8de84c0221a1ee32780aa84dac4d3ea60895e05c))
## v0.2.19 (2025-02-14)
### Bug Fixes
- Get app artifact for installer
([`fe8c3d8`](https://github.com/basicmachines-co/basic-memory/commit/fe8c3d87b003166252290a87cbe958301cccf797))
## v0.2.18 (2025-02-14)
### Bug Fixes
- Don't zip app on release
([`8664c57`](https://github.com/basicmachines-co/basic-memory/commit/8664c57bb331d7f3f7e0239acb5386c7a3c6144e))
## v0.2.17 (2025-02-14)
### Bug Fixes
- Fix app zip in installer release
([`8fa197e`](https://github.com/basicmachines-co/basic-memory/commit/8fa197e2ec8a1b6caaf6dbb39c3c6626bba23e2e))
## v0.2.16 (2025-02-14)
### Bug Fixes
- Debug inspect build on ci
([`1d6054d`](https://github.com/basicmachines-co/basic-memory/commit/1d6054d30a477a4e6a5d6ac885632e50c01945d3))
## v0.2.15 (2025-02-14)
### Bug Fixes
- Debug installer ci
([`dab9573`](https://github.com/basicmachines-co/basic-memory/commit/dab957314aec9ed0e12abca2265552494ae733a2))
## v0.2.14 (2025-02-14)
## v0.2.13 (2025-02-14)
### Bug Fixes
- Refactor release.yml installer
([`a152657`](https://github.com/basicmachines-co/basic-memory/commit/a15265783e47c22d8c7931396281d023b3694e27))
- Try using symlinks in installer build
([`8dd923d`](https://github.com/basicmachines-co/basic-memory/commit/8dd923d5bc0587276f92b5f1db022ad9c8687e45))
## v0.2.12 (2025-02-14)
### Bug Fixes
- Fix cx_freeze options for installer
([`854cf83`](https://github.com/basicmachines-co/basic-memory/commit/854cf8302e2f83578030db05e29b8bdc4348795a))
## v0.2.11 (2025-02-14)
### Bug Fixes
- Ci installer app fix #37
([`2e215fe`](https://github.com/basicmachines-co/basic-memory/commit/2e215fe83ca421b921186c7f1989dc2cb5cca278))
## v0.2.10 (2025-02-14)
### Bug Fixes
- Fix build on github ci for app installer
([`29a2594`](https://github.com/basicmachines-co/basic-memory/commit/29a259421a0ccb10cfa68e3707eaa506ad5e55c0))
## v0.2.9 (2025-02-14)
## v0.2.8 (2025-02-14)
### Bug Fixes
- Fix installer on ci, maybe
([`edbc04b`](https://github.com/basicmachines-co/basic-memory/commit/edbc04be601d234bb1f5eb3ba24d6ad55244b031))
## v0.2.7 (2025-02-14)
### Bug Fixes
- Try to fix installer ci
([`230738e`](https://github.com/basicmachines-co/basic-memory/commit/230738ee9c110c0509e0a09cb0e101a92cfcb729))
## v0.2.6 (2025-02-14)
### Bug Fixes
- Bump project patch version
([`01d4672`](https://github.com/basicmachines-co/basic-memory/commit/01d46727b40c24b017ea9db4b741daef565ac73e))
- Fix installer setup.py change ci to use make
([`3e78fcc`](https://github.com/basicmachines-co/basic-memory/commit/3e78fcc2c208d83467fe7199be17174d7ffcad1a))
## v0.2.5 (2025-02-14)
### Bug Fixes
- Refix vitual env in installer build
([`052f491`](https://github.com/basicmachines-co/basic-memory/commit/052f491fff629e8ead629c9259f8cb46c608d584))
## v0.2.4 (2025-02-14)
## v0.2.3 (2025-02-14)
### Bug Fixes
- Workaround unsigned app
([`41d4d81`](https://github.com/basicmachines-co/basic-memory/commit/41d4d81c1ad1dc2923ba0e903a57454a0c8b6b5c))
## v0.2.2 (2025-02-14)
### Bug Fixes
- Fix path to intaller app artifact
([`53d220d`](https://github.com/basicmachines-co/basic-memory/commit/53d220df585561f9edd0d49a9e88f1d4055059cf))
## v0.2.1 (2025-02-14)
### Bug Fixes
- Activate vitualenv in installer build
([`d4c8293`](https://github.com/basicmachines-co/basic-memory/commit/d4c8293687a52eaf3337fe02e2f7b80e4cc9a1bb))
- Trigger installer build on release
([`f11bf78`](https://github.com/basicmachines-co/basic-memory/commit/f11bf78f3f600d0e1b01996cf8e1f9c39e3dd218))
## v0.2.0 (2025-02-14)
### Features
- Build installer via github action ([#7](https://github.com/basicmachines-co/basic-memory/pull/7),
[`7c381a5`](https://github.com/basicmachines-co/basic-memory/commit/7c381a59c962053c78da096172e484f28ab47e96))
* feat(ci): build installer via github action
* enforce conventional commits in PR titles
* feat: add icon to installer
---------
Co-authored-by: phernandez <phernandez@basicmachines.co>
## v0.1.2 (2025-02-14)
### Bug Fixes
- Fix installer for mac
([`dde9ff2`](https://github.com/basicmachines-co/basic-memory/commit/dde9ff228b72852b5abc58faa1b5e7c6f8d2c477))
- Remove unused FileChange dataclass
([`eb3360c`](https://github.com/basicmachines-co/basic-memory/commit/eb3360cc221f892b12a17137ae740819d48248e8))
- Update uv installer url
([`2f9178b`](https://github.com/basicmachines-co/basic-memory/commit/2f9178b0507b3b69207d5c80799f2d2f573c9a04))
## v0.1.1 (2025-02-07)
## v0.1.0 (2025-02-07)
### Bug Fixes
- Create virtual env in test workflow
([`8092e6d`](https://github.com/basicmachines-co/basic-memory/commit/8092e6d38d536bfb6f93c3d21ea9baf1814f9b0a))
- Fix permalink uniqueness violations on create/update/sync
([`135bec1`](https://github.com/basicmachines-co/basic-memory/commit/135bec181d9b3d53725c8af3a0959ebc1aa6afda))
- Fix recent activity bug
([`3d2c0c8`](https://github.com/basicmachines-co/basic-memory/commit/3d2c0c8c32fcfdaf70a1f96a59d8f168f38a1aa9))
- Install fastapi deps after removing basic-foundation
([`51a741e`](https://github.com/basicmachines-co/basic-memory/commit/51a741e7593a1ea0e5eb24e14c70ff61670f9663))
- Recreate search index on db reset
([`1fee436`](https://github.com/basicmachines-co/basic-memory/commit/1fee436bf903a35c9ebb7d87607fc9cc9f5ff6e7))
- Remove basic-foundation from deps
([`b8d0c71`](https://github.com/basicmachines-co/basic-memory/commit/b8d0c7160f29c97cdafe398a7e6a5240473e0c89))
- Run tests via uv
([`4eec820`](https://github.com/basicmachines-co/basic-memory/commit/4eec820a32bc059a405e2f4dac4c73b245ca4722))
### Chores
- Rename import tool
([`af6b7dc`](https://github.com/basicmachines-co/basic-memory/commit/af6b7dc40a55eaa2aa78d6ea831e613851081d52))
### Features
- Add memory-json importer, tweak observation content
([`3484e26`](https://github.com/basicmachines-co/basic-memory/commit/3484e26631187f165ee6eb85517e94717b7cf2cf))
## v0.0.1 (2025-02-04)
### Bug Fixes
- Fix versioning for 0.0.1 release
([`ba1e494`](https://github.com/basicmachines-co/basic-memory/commit/ba1e494ed1afbb7af3f97c643126bced425da7e0))
## v0.0.0 (2025-02-04)
### Chores
- Remove basic-foundation src ref in pyproject.toml
([`29fce8b`](https://github.com/basicmachines-co/basic-memory/commit/29fce8b0b922d54d7799bf2534107ee6cfb961b8))
-10
View File
@@ -1,10 +0,0 @@
cff-version: 1.0.3
message: "If you use this project, please cite it as follows:"
authors:
- family-names: "Hernandez"
given-names: "Paul"
affiliation: "Basic Machines"
title: "Basic Memory"
version: "0.0.1"
date-released: "2025-02-03"
url: "https://github.com/basicmachines-co/basic-memory"
-34
View File
@@ -1,34 +0,0 @@
Developer Certificate of Origin
Version 1.1
https://developercertificate.org/
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
-224
View File
@@ -1,224 +0,0 @@
# CLAUDE.md - Basic Memory Project Guide
## Project Overview
Basic Memory is a local-first knowledge management system built on the Model Context Protocol (MCP). It enables
bidirectional communication between LLMs (like Claude) and markdown files, creating a personal knowledge graph that can
be traversed using links between documents.
## CODEBASE DEVELOPMENT
### Project information
See the [README.md](README.md) file for a project overview.
### Build and Test Commands
- Install: `make install` or `pip install -e ".[dev]"`
- Run tests: `uv run pytest -p pytest_mock -v` or `make test`
- Single test: `pytest tests/path/to/test_file.py::test_function_name`
- Lint: `make lint` or `ruff check . --fix`
- Type check: `make type-check` or `uv run pyright`
- Format: `make format` or `uv run ruff format .`
- Run all code checks: `make check` (runs lint, format, type-check, test)
- Create db migration: `make migration m="Your migration message"`
- Run development MCP Inspector: `make run-inspector`
### Code Style Guidelines
- Line length: 100 characters max
- Python 3.12+ with full type annotations
- Format with ruff (consistent styling)
- Import order: standard lib, third-party, local imports
- Naming: snake_case for functions/variables, PascalCase for classes
- Prefer async patterns with SQLAlchemy 2.0
- Use Pydantic v2 for data validation and schemas
- CLI uses Typer for command structure
- API uses FastAPI for endpoints
- Follow the repository pattern for data access
- Tools communicate to api routers via the httpx ASGI client (in process)
- avoid using "private" functions in modules or classes (prepended with _)
### Codebase Architecture
- `/alembic` - Alembic db migrations
- `/api` - FastAPI implementation of REST endpoints
- `/cli` - Typer command-line interface
- `/markdown` - Markdown parsing and processing
- `/mcp` - Model Context Protocol server implementation
- `/models` - SQLAlchemy ORM models
- `/repository` - Data access layer
- `/schemas` - Pydantic models for validation
- `/services` - Business logic layer
- `/sync` - File synchronization services
### Development Notes
- MCP tools are defined in src/basic_memory/mcp/tools/
- MCP prompts are defined in src/basic_memory/mcp/prompts/
- MCP tools should be atomic, composable operations
- Use `textwrap.dedent()` for multi-line string formatting in prompts and tools
- MCP Prompts are used to invoke tools and format content with instructions for an LLM
- Schema changes require Alembic migrations
- SQLite is used for indexing and full text search, files are source of truth
- Testing uses pytest with asyncio support (strict mode)
- Test database uses in-memory SQLite
- Avoid creating mocks in tests in most circumstances.
- Each test runs in a standalone environment with in memory SQLite and tmp_file directory
- Do not use mocks in tests if possible. Tests run with an in memory sqlite db, so they are not needed. See fixtures in conftest.py
## BASIC MEMORY PRODUCT USAGE
### Knowledge Structure
- Entity: Any concept, document, or idea represented as a markdown file
- Observation: A categorized fact about an entity (`- [category] content`)
- Relation: A directional link between entities (`- relation_type [[Target]]`)
- Frontmatter: YAML metadata at the top of markdown files
- Knowledge representation follows precise markdown format:
- Observations with [category] prefixes
- Relations with WikiLinks [[Entity]]
- Frontmatter with metadata
### Basic Memory Commands
- Sync knowledge: `basic-memory sync` or `basic-memory sync --watch`
- Import from Claude: `basic-memory import claude conversations`
- Import from ChatGPT: `basic-memory import chatgpt`
- Import from Memory JSON: `basic-memory import memory-json`
- Check sync status: `basic-memory status`
- Tool access: `basic-memory tools` (provides CLI access to MCP tools)
- Guide: `basic-memory tools basic-memory-guide`
- Continue: `basic-memory tools continue-conversation --topic="search"`
### MCP Capabilities
- Basic Memory exposes these MCP tools to LLMs:
**Content Management:**
- `write_note(title, content, folder, tags)` - Create/update markdown notes with semantic observations and relations
- `read_note(identifier, page, page_size)` - Read notes by title, permalink, or memory:// URL with knowledge graph
awareness
- `read_file(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
**Knowledge Graph Navigation:**
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation
continuity
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "
1d", "1 week")
**Search & Discovery:**
- `search_notes(query, page, page_size)` - Full-text search across all content with filtering options
**Visualization:**
- `canvas(nodes, edges, title, folder)` - Generate Obsidian canvas files for knowledge graph visualization
- MCP Prompts for better AI interaction:
- `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
- `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
- `search_notes(query, after_date)` - Search with detailed, formatted results for better context understanding
- `recent_activity(timeframe)` - View recently changed items with formatted output
- `json_canvas_spec()` - Full JSON Canvas specification for Obsidian visualization
## AI-Human Collaborative Development
Basic Memory emerged from and enables a new kind of development process that combines human and AI capabilities. Instead
of using AI just for code generation, we've developed a true collaborative workflow:
1. AI (LLM) writes initial implementation based on specifications and context
2. Human reviews, runs tests, and commits code with any necessary adjustments
3. Knowledge persists across conversations using Basic Memory's knowledge graph
4. Development continues seamlessly across different AI sessions with consistent context
5. Results improve through iterative collaboration and shared understanding
This approach has allowed us to tackle more complex challenges and build a more robust system than either humans or AI
could achieve independently.
## GitHub Integration
Basic Memory uses Claude directly into the development workflow through GitHub:
### GitHub MCP Tools
Using the GitHub Model Context Protocol server, Claude can:
- **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
With this integration, the AI assistant is a full-fledged team member rather than just a tool for generating code
snippets.
### Basic Memory Pro
Basic Memory Pro is a desktop GUI application that wraps the basic-memory CLI/MCP tools:
- Built with Tauri (Rust), React (TypeScript), and a Python FastAPI sidecar
- Provides visual knowledge graph exploration and project management
- Uses the same core codebase but adds a desktop-friendly interface
- Project configuration is shared between CLI and Pro versions
- Multiple project support with visual switching interface
local repo: /Users/phernandez/dev/basicmachines/basic-memory-pro
github: https://github.com/basicmachines-co/basic-memory-pro
## Release and Version Management
Basic Memory uses `uv-dynamic-versioning` for automatic version management based on git tags:
### Version Types
- **Development versions**: Automatically generated from commits (e.g., `0.12.4.dev26+468a22f`)
- **Beta releases**: Created by tagging with beta suffixes (e.g., `v0.13.0b1`, `v0.13.0rc1`)
- **Stable releases**: Created by tagging with version numbers (e.g., `v0.13.0`)
### Release Workflows
#### Development Builds (Automatic)
- Triggered on every push to `main` branch
- Publishes dev versions like `0.12.4.dev26+468a22f` to PyPI
- Allows continuous testing of latest changes
- Users install with: `pip install basic-memory --pre --force-reinstall`
#### Beta/RC Releases (Manual)
- Create beta tag: `git tag v0.13.0b1 && git push origin v0.13.0b1`
- Automatically builds and publishes to PyPI as pre-release
- Users install with: `pip install basic-memory --pre`
- Use for milestone testing before stable release
#### Stable Releases (Manual)
- Create version tag: `git tag v0.13.0 && git push origin v0.13.0`
- Automatically builds, creates GitHub release, and publishes to PyPI
- Users install with: `pip install basic-memory`
### For Development
- No manual version bumping required
- Versions automatically derived from git tags
- `pyproject.toml` uses `dynamic = ["version"]`
- `__init__.py` dynamically reads version from package metadata
-19
View File
@@ -1,19 +0,0 @@
# Code of Conduct
## Purpose
Maintain a respectful and professional environment where contributions can be made without harassment or
negativity.
## Standards
Respectful communication and collaboration are expected. Offensive behavior, harassment, or personal attacks will not be
tolerated.
## Reporting Issues
To report inappropriate behavior, contact [paul@basicmachines.co].
## Consequences
Violations of this code may lead to consequences, including being banned from contributing to the project.
-197
View File
@@ -1,197 +0,0 @@
# 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.
## Getting Started
### Development Environment
1. **Clone the Repository**:
```bash
git clone https://github.com/basicmachines-co/basic-memory.git
cd basic-memory
```
2. **Install Dependencies**:
```bash
# Using make (recommended)
make install
# Or using uv
uv install -e ".[dev]"
# Or using pip
pip install -e ".[dev]"
```
3. **Run the Tests**:
```bash
# Run all tests
make test
# or
uv run pytest -p pytest_mock -v
# Run a specific test
pytest tests/path/to/test_file.py::test_function_name
```
### Development Workflow
1. **Fork the Repo**: Fork the repository on GitHub and clone your copy.
2. **Create a Branch**: Create a new branch for your feature or fix.
```bash
git checkout -b feature/your-feature-name
# or
git checkout -b fix/issue-you-are-fixing
```
3. **Make Your Changes**: Implement your changes with appropriate test coverage.
4. **Check Code Quality**:
```bash
# Run all checks at once
make check
# Or run individual checks
make lint # Run linting
make format # Format code
make type-check # Type checking
```
5. **Test Your Changes**: Ensure all tests pass locally and maintain 100% test coverage.
```bash
make test
```
6. **Submit a PR**: Submit a pull request with a detailed description of your changes.
## LLM-Assisted Development
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
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
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
## 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.
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)
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:
- 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.
### Signing Your Commits
Sign your commit:
**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.
The sign-off certifies that you have the right to submit your contribution under the project's license and verifies your
agreement to the DCO.
## Code Style Guidelines
- **Python Version**: Python 3.12+ with full type annotations
- **Line Length**: 100 characters maximum
- **Formatting**: Use ruff for consistent styling
- **Import Order**: Standard lib, third-party, local imports
- **Naming**: Use snake_case for functions/variables, PascalCase for classes
- **Documentation**: Add docstrings to public functions, classes, and methods
- **Type Annotations**: Use type hints for all functions and methods
## Testing Guidelines
- **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 only when necessary
- **Edge Cases**: Test both normal operation and edge cases
- **Database Testing**: Use in-memory SQLite for testing database operations
- **Fixtures**: Use async pytest fixtures for setup and teardown
## Release Process
Basic Memory uses automatic versioning based on git tags with `uv-dynamic-versioning`. Here's how releases work:
### Version Management
- **Development versions**: Automatically generated from git commits (e.g., `0.12.4.dev26+468a22f`)
- **Beta releases**: Created by tagging with beta suffixes (e.g., `git tag v0.13.0b1`)
- **Stable releases**: Created by tagging with version numbers (e.g., `git tag v0.13.0`)
### Release Workflows
#### Development Builds
- Automatically published to PyPI on every commit to `main`
- Version format: `0.12.4.dev26+468a22f` (base version + dev + commit count + hash)
- Users install with: `pip install basic-memory --pre --force-reinstall`
#### Beta Releases
1. Create and push a beta tag: `git tag v0.13.0b1 && git push origin v0.13.0b1`
2. GitHub Actions automatically builds and publishes to PyPI
3. Users install with: `pip install basic-memory --pre`
#### Stable Releases
1. Create and push a version tag: `git tag v0.13.0 && git push origin v0.13.0`
2. GitHub Actions automatically:
- Builds the package with version `0.13.0`
- Creates GitHub release with auto-generated notes
- Publishes to PyPI
3. Users install with: `pip install basic-memory`
### For Contributors
- No manual version bumping required
- Versions are automatically derived from git tags
- Focus on code changes, not version management
## Creating Issues
If you're planning to work on something, please create an issue first to discuss the approach. Include:
- A clear title and description
- Steps to reproduce if reporting a bug
- Expected behavior vs. actual behavior
- Any relevant logs or screenshots
- Your proposed solution, if you have one
## Code of Conduct
All contributors must follow the [Code of Conduct](CODE_OF_CONDUCT.md).
## Thank You!
Your contributions help make Basic Memory better. We appreciate your time and effort!
-16
View File
@@ -1,16 +0,0 @@
# 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"]
-661
View File
@@ -1,661 +0,0 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
-59
View File
@@ -1,59 +0,0 @@
.PHONY: install test test-module lint clean format type-check installer-mac installer-win check test-int
install:
pip install -e ".[dev]"
test-unit:
uv run pytest -p pytest_mock -v
test-int:
uv run pytest -p pytest_mock -v --no-cov test-int
test: test-unit test-int
lint:
ruff check . --fix
type-check:
uv run pyright
clean:
find . -type f -name '*.pyc' -delete
find . -type d -name '__pycache__' -exec rm -r {} +
rm -rf installer/build/
rm -rf installer/dist/
rm -f rw.*.dmg
rm -rf dist
rm -rf installer/build
rm -rf installer/dist
rm -f .coverage.*
format:
uv run ruff format .
# run inspector tool
run-inspector:
npx @modelcontextprotocol/inspector
# Build app installer
installer-mac:
cd installer && chmod +x make_icons.sh && ./make_icons.sh
cd installer && uv run python setup.py bdist_mac
installer-win:
cd installer && uv run python setup.py bdist_win32
update-deps:
uv lock --upgrade
check: lint format type-check test
# Target for generating Alembic migrations with a message from command line
migration:
@if [ -z "$(m)" ]; then \
echo "Usage: make migration m=\"Your migration message\""; \
exit 1; \
fi; \
cd src/basic_memory/alembic && alembic revision --autogenerate -m "$(m)"
-414
View File
@@ -1,414 +0,0 @@
[![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)
![](https://badge.mcpx.dev?type=server 'MCP Server')
![](https://badge.mcpx.dev?type=dev 'MCP Dev')
[![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.
- Website: https://basicmachines.co
- Documentation: https://memory.basicmachines.co
## Pick up your conversation right where you left off
- 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 tool install basic-memory
# Configure Claude Desktop (edit ~/Library/Application Support/Claude/claude_desktop_config.json)
# Add this to your config:
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": [
"basic-memory",
"mcp"
]
}
}
}
# Now in Claude Desktop, you can:
# - Write notes with "Create a note about coffee brewing methods"
# - Read notes with "What do I know about pour over coffee?"
# - Search with "Find information about Ethiopian beans"
```
You can view shared context via files in `~/basic-memory` (default directory location).
### Alternative Installation via Smithery
You can use [Smithery](https://smithery.ai/server/@basicmachines-co/basic-memory) to automatically configure Basic
Memory for Claude Desktop:
```bash
npx -y @smithery/cli install @basicmachines-co/basic-memory --client claude
```
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.
### Glama.ai
<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?
Most LLM interactions are ephemeral - you ask a question, get an answer, and everything is forgotten. Each conversation
starts fresh, without the context or knowledge from previous ones. Current workarounds have limitations:
- Chat histories capture conversations but aren't structured knowledge
- RAG systems can query documents but don't let LLMs write back
- Vector databases require complex setups and often live in the cloud
- Knowledge graphs typically need specialized tools to maintain
Basic Memory addresses these problems with a simple approach: structured Markdown files that both humans and LLMs can
read
and write to. The key advantages:
- **Local-first:** All knowledge stays in files you control
- **Bi-directional:** Both you and the LLM read and write to the same files
- **Structured yet simple:** Uses familiar Markdown with semantic patterns
- **Traversable knowledge graph:** LLMs can follow links between topics
- **Standard formats:** Works with existing editors like Obsidian
- **Lightweight infrastructure:** Just local files indexed in a local SQLite database
With Basic Memory, you can:
- Have conversations that build on previous knowledge
- Create structured notes during natural conversations
- Have conversations with LLMs that remember what you've discussed before
- Navigate your knowledge graph semantically
- Keep everything local and under your control
- Use familiar tools like Obsidian to view and edit notes
- Build a personal knowledge base that grows over time
## How It Works in Practice
Let's say you're exploring coffee brewing methods and want to capture your knowledge. Here's how it works:
1. Start by chatting normally:
```
I've been experimenting with different coffee brewing methods. Key things I've learned:
- Pour over gives more clarity in flavor than French press
- Water temperature is critical - around 205°F seems best
- Freshly ground beans make a huge difference
```
... continue conversation.
2. Ask the LLM to help structure this knowledge:
```
"Let's write a note about coffee brewing methods."
```
LLM creates a new Markdown file on your system (which you can see instantly in Obsidian or your editor):
```markdown
---
title: Coffee Brewing Methods
permalink: coffee-brewing-methods
tags:
- coffee
- brewing
---
# 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
## Relations
- relates_to [[Coffee Bean Origins]]
- requires [[Proper Grinding Technique]]
- affects [[Flavor Extraction]]
```
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 current project directory (default `~/$HOME/basic-memory`).
- Realtime sync is enabled by default with the v0.12.0 version
4. In a chat with the LLM, you can reference a topic:
```
Look at `coffee-brewing-methods` for context about pour over coffee
```
The LLM can now build rich context from the knowledge graph. For example:
```
Following relation 'relates_to [[Coffee Bean Origins]]':
- Found information about Ethiopian Yirgacheffe
- Notes on Colombian beans' nutty profile
- Altitude effects on bean characteristics
Following relation 'requires [[Proper Grinding Technique]]':
- Burr vs. blade grinder comparisons
- Grind size recommendations for different methods
- Impact of consistent particle size on extraction
```
Each related document can lead to more context, building a rich semantic understanding of your knowledge base.
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.
## Technical Implementation
Under the hood, Basic Memory:
1. Stores everything in Markdown files
2. Uses a SQLite database for searching and indexing
3. Extracts semantic meaning from simple Markdown patterns
- Files become `Entity` objects
- Each `Entity` can have `Observations`, or facts associated with it
- `Relations` connect entities together to form the knowledge graph
4. Maintains the local knowledge graph derived from the files
5. Provides bidirectional synchronization between files and the knowledge graph
6. Implements the Model Context Protocol (MCP) for AI integration
7. Exposes tools that let AI assistants traverse and manipulate the knowledge graph
8. Uses memory:// URLs to reference entities across tools and conversations
The file format is just Markdown with some simple markup:
Each Markdown file has:
### Frontmatter
```markdown
title: <Entity title>
type: <The type of Entity> (e.g. note)
permalink: <a uri slug>
- <optional metadata> (such as tags)
```
### Observations
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
"#" character, and an optional `context`.
Observation Markdown format:
```markdown
- [category] content #tag (optional context)
```
Examples of observations:
```markdown
- [method] Pour over extracts more floral notes than French press
- [tip] Grind size should be medium-fine for pour over #brewing
- [preference] Ethiopian beans have bright, fruity flavors (especially from Yirgacheffe)
- [fact] Lighter roasts generally contain more caffeine than dark roasts
- [experiment] Tried 1:15 coffee-to-water ratio with good results
- [resource] James Hoffman's V60 technique on YouTube is excellent
- [question] Does water temperature affect extraction of different compounds differently?
- [note] My favorite local shop uses a 30-second bloom time
```
### Relations
Relations are links to other topics. They define how entities connect in the knowledge graph.
Markdown format:
```markdown
- relation_type [[WikiLink]] (optional context)
```
Examples of relations:
```markdown
- pairs_well_with [[Chocolate Desserts]]
- grown_in [[Ethiopia]]
- contrasts_with [[Tea Brewing Methods]]
- requires [[Burr Grinder]]
- improves_with [[Fresh Beans]]
- relates_to [[Morning Routine]]
- inspired_by [[Japanese Coffee Culture]]
- documented_in [[Coffee Journal]]
```
## Using with VS Code
For one-click installation, click one of the install buttons below...
[![Install with UV in VS Code](https://img.shields.io/badge/VS_Code-UV-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D) [![Install with UV in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-UV-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D&quality=insiders)
You can use Basic Memory with VS Code to easily retrieve and store information while coding. Click the installation buttons above for one-click setup, or follow the manual installation instructions below.
### Manual Installation
Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`.
```json
{
"mcp": {
"servers": {
"basic-memory": {
"command": "uvx",
"args": ["basic-memory", "mcp"]
}
}
}
}
```
Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace. This will allow you to share the configuration with others.
```json
{
"servers": {
"basic-memory": {
"command": "uvx",
"args": ["basic-memory", "mcp"]
}
}
}
```
## Using with Claude Desktop
Basic Memory is built using the MCP (Model Context Protocol) and works with the Claude desktop app (https://claude.ai/):
1. Configure Claude Desktop to use Basic Memory:
Edit your MCP configuration file (usually located at `~/Library/Application Support/Claude/claude_desktop_config.json`
for OS X):
```json
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": [
"basic-memory",
"mcp"
]
}
}
}
```
If you want to use a specific project (see [Multiple Projects](docs/User%20Guide.md#multiple-projects)), update your
Claude Desktop
config:
```json
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": [
"basic-memory",
"--project",
"your-project-name",
"mcp"
]
}
}
}
```
2. Sync your knowledge:
Basic Memory will sync the files in your project in real time if you make manual edits.
3. In Claude Desktop, the LLM can now use these tools:
```
write_note(title, content, folder, tags) - Create or update notes
read_note(identifier, page, page_size) - Read notes by title or permalink
build_context(url, depth, timeframe) - Navigate knowledge graph via memory:// URLs
search_notes(query, page, page_size) - Search across your knowledge base
recent_activity(type, depth, timeframe) - Find recently updated information
canvas(nodes, edges, title, folder) - Generate knowledge visualizations
```
5. Example prompts to try:
```
"Create a note about our project architecture decisions"
"Find information about JWT authentication in my notes"
"Create a canvas visualization of my project components"
"Read my notes on the authentication system"
"What have I been working on in the past week?"
```
## Futher info
See the [Documentation](https://memory.basicmachines.co/) for more info, including:
- [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)
## Installation Options
### Stable Release
```bash
pip install basic-memory
```
### Beta/Pre-releases
```bash
pip install basic-memory --pre
```
### Development Builds
Development versions are automatically published on every commit to main with versions like `0.12.4.dev26+468a22f`:
```bash
pip install basic-memory --pre --force-reinstall
```
## License
AGPL-3.0
Contributions are welcome. See the [Contributing](CONTRIBUTING.md) guide for info about setting up the project locally
and submitting PRs.
## Star History
<a href="https://www.star-history.com/#basicmachines-co/basic-memory&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=basicmachines-co/basic-memory&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=basicmachines-co/basic-memory&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=basicmachines-co/basic-memory&type=Date" />
</picture>
</a>
Built with ♥️ by Basic Machines
-237
View File
@@ -1,237 +0,0 @@
# Release Notes v0.13.0
## Overview
Basic Memory v0.13.0 is a **major release** that transforms Basic Memory into a true multi-project knowledge management system. This release introduces fluid project switching, advanced note editing capabilities, robust file management, and production-ready OAuth authentication - all while maintaining full backward compatibility.
**What's New for Users:**
- 🎯 **Switch between projects instantly** during conversations with Claude
- ✏️ **Edit notes incrementally** without rewriting entire documents
- 📁 **Move and organize notes** with full database consistency
- 🔍 **Search frontmatter tags** to discover content more easily
- 🔐 **OAuth authentication** for secure remote access
-**Development builds** automatically published for beta testing
**Key v0.13.0 Accomplishments:**
-**Complete Project Management System** - Project switching and project-specific operations
-**Advanced Note Editing** - Incremental editing with append, prepend, find/replace, and section operations
-**File Management System** - Full move operations with database consistency and rollback protection
-**Enhanced Search Capabilities** - Frontmatter tags now searchable, improved content discoverability
-**Unified Database Architecture** - Single app-level database for better performance and project management
## Major Features
### 1. Multiple Project Management 🎯
**Switch between projects instantly during conversations:**
```
💬 "What projects do I have?"
🤖 Available projects:
• main (current, default)
• work-notes
• personal-journal
• code-snippets
💬 "Switch to work-notes"
🤖 ✓ Switched to work-notes project
Project Summary:
• 47 entities
• 125 observations
• 23 relations
💬 "What did I work on yesterday?"
🤖 [Shows recent activity from work-notes project]
```
**Key Capabilities:**
- **Instant Project Switching**: Change project context mid-conversation without restart
- **Project-Specific Operations**: Operations work within the currently active project context
- **Project Discovery**: List all available projects with status indicators
- **Session Context**: Maintains active project throughout conversation
- **Backward Compatibility**: Existing single-project setups continue to work seamlessly
### 2. Advanced Note Editing ✏️
**Edit notes incrementally without rewriting entire documents:**
```python
# Append new sections to existing notes
edit_note("project-planning", "append", "\n## New Requirements\n- Feature X\n- Feature Y")
# Prepend timestamps to meeting notes
edit_note("meeting-notes", "prepend", "## 2025-05-27 Update\n- Progress update...")
# Replace specific sections under headers
edit_note("api-spec", "replace_section", "New implementation details", section="## Implementation")
# Find and replace with validation
edit_note("config", "find_replace", "v0.13.0", find_text="v0.12.0", expected_replacements=2)
```
**Key Capabilities:**
- **Append Operations**: Add content to end of notes (most common use case)
- **Prepend Operations**: Add content to beginning of notes
- **Section Replacement**: Replace content under specific markdown headers
- **Find & Replace**: Simple text replacements with occurrence counting
- **Smart Error Handling**: Helpful guidance when operations fail
- **Project Context**: Works within the active project with session awareness
### 3. Smart File Management 📁
**Move and organize notes:**
```python
# Simple moves with automatic folder creation
move_note("my-note", "work/projects/my-note.md")
# Organize within the active project
move_note("shared-doc", "archive/old-docs/shared-doc.md")
# Rename operations
move_note("old-name", "same-folder/new-name.md")
```
**Key Capabilities:**
- **Database Consistency**: Updates file paths, permalinks, and checksums automatically
- **Search Reindexing**: Maintains search functionality after moves
- **Folder Creation**: Automatically creates destination directories
- **Project Isolation**: Operates within the currently active project
- **Link Preservation**: Maintains internal links and references
### 4. Enhanced Search & Discovery 🔍
**Find content more easily with improved search capabilities:**
- **Frontmatter Tag Search**: Tags from YAML frontmatter are now indexed and searchable
- **Improved Content Discovery**: Search across titles, content, tags, and metadata
- **Project-Scoped Search**: Search within the currently active project
- **Better Search Quality**: Enhanced FTS5 indexing with tag content inclusion
**Example:**
```yaml
---
title: Coffee Brewing Methods
tags: [coffee, brewing, equipment]
---
```
Now searchable by: "coffee", "brewing", "equipment", or "Coffee Brewing Methods"
### 5. Unified Database Architecture 🗄️
**Single app-level database for better performance and project management:**
- **Migration from Per-Project DBs**: Moved from multiple SQLite files to single app database
- **Project Isolation**: Proper data separation with project_id foreign keys
- **Better Performance**: Optimized queries and reduced file I/O
## Complete MCP Tool Suite 🛠️
### New Project Management Tools
- **`list_projects()`** - Discover and list all available projects with status
- **`switch_project(project_name)`** - Change active project context during conversations
- **`get_current_project()`** - Show currently active project with statistics
- **`set_default_project(project_name)`** - Update default project configuration
### New Note Operations Tools
- **`edit_note()`** - Incremental note editing (append, prepend, find/replace, section replace)
- **`move_note()`** - Move notes with database consistency and search reindexing
### Enhanced Existing Tools
All existing tools now support:
- **Session context awareness** (operates within the currently active project)
- **Enhanced error messages** with project context metadata
- **Improved response formatting** with project information footers
- **Project isolation** ensures operations stay within the correct project boundaries
## User Experience Improvements
### Installation Options
**Multiple ways to install and test Basic Memory:**
```bash
# Stable release
uv tool install basic-memory
# Beta/pre-releases
uv tool install basic-memory --pre
```
### Bug Fixes & Quality Improvements
**Major issues resolved in v0.13.0:**
- **#118**: Fixed YAML tag formatting to follow standard specification
- **#110**: Fixed `--project` flag consistency across all CLI commands
- **#107**: Fixed write_note update failures with existing notes
- **#93**: Fixed custom permalink handling in frontmatter
- **#52**: Enhanced search capabilities with frontmatter tag indexing
- **FTS5 Search**: Fixed special character handling in search queries
- **Error Handling**: Improved error messages and validation across all tools
## Breaking Changes & Migration
### For Existing Users
**Automatic Migration**: First run will automatically migrate existing data to the new unified database structure. No manual action required.
**What Changes:**
- Database location: Moved to `~/.basic-memory/memory.db` (unified across projects)
- Configuration: Projects defined in `~/.basic-memory/config.json` are synced with database
**What Stays the Same:**
- All existing notes and data remain unchanged
- Default project behavior maintained for single-project users
- All existing MCP tools continue to work without modification
## Documentation & Resources
### New Documentation
- [Project Management Guide](docs/Project%20Management.md) - Multi-project workflows
- [Note Editing Guide](docs/Note%20Editing.md) - Advanced editing techniques
### Updated Documentation
- [README.md](README.md) - Installation options and beta build instructions
- [CONTRIBUTING.md](CONTRIBUTING.md) - Release process and version management
- [CLAUDE.md](CLAUDE.md) - Development workflow and CI/CD documentation
- [Claude.ai Integration](docs/Claude.ai%20Integration.md) - Updated MCP tool examples
### Quick Start Examples
**Project Switching:**
```
💬 "Switch to my work project and show recent activity"
🤖 [Calls switch_project("work") then recent_activity()]
```
**Note Editing:**
```
💬 "Add a section about deployment to my API docs"
🤖 [Calls edit_note("api-docs", "append", "## Deployment\n...")]
```
**File Organization:**
```
💬 "Move my old meeting notes to the archive folder"
🤖 [Calls move_note("meeting-notes", "archive/old-meetings.md")]
```
### Getting Updates
```bash
# Stable releases
uv tool upgrade basic-memory
# Beta releases
uv tool install basic-memory --pre --force-reinstall
# Latest development
uv tool install basic-memory --pre --force-reinstall
```
-13
View File
@@ -1,13 +0,0 @@
# 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
-337
View File
@@ -1,337 +0,0 @@
# Manual Testing Suite for Basic Memory
This document outlines a comprehensive manual testing approach where an AI assistant (Claude) executes real-world usage scenarios using Basic Memory's MCP tools. The unique aspect: **Basic Memory tests itself** - all test observations and results are recorded as notes in a dedicated test project.
## Philosophy
- **Integration over Isolation**: Test the full MCP→API→DB→File stack
- **Real Usage Patterns**: Creative exploration, not just checklist validation
- **Self-Documenting**: Use Basic Memory to record all test observations
- **Living Documentation**: Test results become part of the knowledge base
## Setup Instructions
### 1. Environment Preparation
```bash
# Ensure latest basic-memory is installed
pip install --upgrade basic-memory
# Verify MCP server is available
basic-memory --version
```
### 2. MCP Integration Setup
**Option A: Claude Desktop Integration**
```json
// Add to ~/.config/claude-desktop/claude_desktop_config.json
// or
// .mcp.json
{
"mcpServers": {
"basic-memory": {
"command": "uv",
"args": [
"--directory",
"/Users/phernandez/dev/basicmachines/basic-memory",
"run",
"src/basic_memory/cli/main.py",
"mcp"
]
}
}
}
```
**Option B: Claude Code MCP**
```bash
claude mcp add basic-memory basic-memory mcp
```
### 3. Test Project Creation
During testing, create a dedicated test project:
```
- Project name: "basic-memory-testing"
- Location: ~/basic-memory-testing
- Purpose: Contains all test observations and results
```
## Testing Categories
### Phase 1: Core Functionality Validation
**Objective**: Verify all basic operations work correctly
**Test Areas:**
- [ ] **Note Creation**: Various content types, structures, frontmatter
- [ ] **Note Reading**: By title, path, memory:// URLs, non-existent notes
- [ ] **Search Operations**: Simple queries, boolean operators, tag searches
- [ ] **Context Building**: Different depths, timeframes, relation traversal
- [ ] **Recent Activity**: Various timeframes, filtering options
**Success Criteria:**
- All operations complete without errors
- Files appear correctly in filesystem
- Search returns expected results
- Context includes appropriate related content
**Observations to Record:**
```markdown
# Core Functionality Test Results
## Test Execution
- [timestamp] Test started at 2025-01-06 15:30:00
- [setup] Created test project successfully
- [environment] MCP connection established
## write_note Tests
- [success] Basic note creation works
- [success] Frontmatter tags are preserved
- [issue] Special characters in titles need investigation
## Relations
- validates [[Search Operations Test]]
- part_of [[Manual Testing Suite]]
```
### Phase 2: v0.13.0 Feature Deep Dive
**Objective**: Thoroughly test new project management and editing capabilities
**Project Management Tests:**
- [ ] Create multiple projects dynamically
- [ ] Switch between projects mid-conversation
- [ ] Cross-project operations (create notes in different projects)
- [ ] Project discovery and status checking
- [ ] Default project behavior
**Note Editing Tests:**
- [ ] Append operations (add content to end)
- [ ] Prepend operations (add content to beginning)
- [ ] Find/replace operations with validation
- [ ] Section replacement under headers
- [ ] Edit operations across different projects
**File Management Tests:**
- [ ] Move notes within same project
- [ ] Move notes between projects
- [ ] Automatic folder creation during moves
- [ ] Move operations with special characters
- [ ] Database consistency after moves
**Success Criteria:**
- Project switching preserves context correctly
- Edit operations modify files as expected
- Move operations maintain database consistency
- Search indexes update after moves and edits
### Phase 3: Edge Case Exploration
**Objective**: Discover limits and handle unusual scenarios gracefully
**Boundary Testing:**
- [ ] Very long note titles and content
- [ ] Empty notes and projects
- [ ] Special characters: unicode, emojis, symbols
- [ ] Deeply nested folder structures
- [ ] Circular relations and self-references
**Error Scenario Testing:**
- [ ] Invalid memory:// URLs
- [ ] Missing files referenced in database
- [ ] Concurrent operations (if possible)
- [ ] Invalid project names
- [ ] Disk space constraints (if applicable)
**Performance Testing:**
- [ ] Large numbers of notes (100+)
- [ ] Complex search queries
- [ ] Deep relation chains (5+ levels)
- [ ] Rapid successive operations
### Phase 4: Real-World Workflow Scenarios
**Objective**: Test realistic usage patterns that users might follow
**Scenario 1: Meeting Notes Pipeline**
1. Create meeting notes with action items
2. Extract action items into separate notes
3. Link to project planning documents
4. Update progress over time using edit operations
5. Archive completed items
**Scenario 2: Research Knowledge Building**
1. Create research topic notes
2. Build complex relation networks
3. Add incremental findings over time
4. Search and discover connections
5. Reorganize as knowledge grows
**Scenario 3: Multi-Project Workflow**
1. Work project: Technical documentation
2. Personal project: Recipe collection
3. Learning project: Course notes
4. Switch between projects during conversation
5. Cross-reference related concepts
**Scenario 4: Content Evolution**
1. Start with basic notes
2. Gradually enhance with relations
3. Reorganize file structure
4. Update existing content incrementally
5. Build comprehensive knowledge graph
### Phase 5: Creative Stress Testing
**Objective**: Push the system to discover unexpected behaviors
**Creative Exploration Areas:**
- [ ] Rapid project creation and switching
- [ ] Unusual but valid markdown structures
- [ ] Creative use of observation categories
- [ ] Novel relation types and patterns
- [ ] Combining tools in unexpected ways
**Stress Scenarios:**
- [ ] Bulk operations (create many notes quickly)
- [ ] Complex nested moves and edits
- [ ] Deep context building with large graphs
- [ ] Search with complex boolean expressions
## Test Execution Process
### Pre-Test Checklist
- [ ] MCP connection verified
- [ ] Test project created
- [ ] Baseline notes recorded
### During Testing
1. **Execute test scenarios** using actual MCP tool calls
2. **Record observations** immediately in test project
3. **Note timestamps** for performance tracking
4. **Document any errors** with reproduction steps
5. **Explore variations** when something interesting happens
### Test Observation Format
Record all observations as Basic Memory notes using this structure:
```markdown
---
title: Test Session YYYY-MM-DD HH:MM
tags: [testing, session, v0.13.0]
---
# Test Session YYYY-MM-DD HH:MM
## Test Focus
- Primary objective
- Features being tested
## Observations
- [success] Feature X worked as expected #functionality
- [performance] Operation Y took 2.3 seconds #timing
- [issue] Error with special characters #bug
- [enhancement] Could improve UX for scenario Z #improvement
## Discovered Issues
- [bug] Description of problem with reproduction steps
- [limitation] Current system boundary encountered
## Relations
- tests [[Feature X]]
- part_of [[Manual Testing Suite]]
- found_issue [[Bug Report: Special Characters]]
```
### Post-Test Analysis
- [ ] Review all test observations
- [ ] Create summary report with findings
- [ ] Identify patterns in successes/failures
- [ ] Generate improvement recommendations
## Success Metrics
**Quantitative Measures:**
- % of test scenarios completed successfully
- Number of bugs discovered and documented
- Performance benchmarks established
- Coverage of all MCP tools and operations
**Qualitative Measures:**
- Natural conversation flow maintained
- Knowledge graph quality and connections
- User experience insights captured
- System reliability under various conditions
## Expected Outcomes
**For the System:**
- Validation of v0.13.0 features in real usage
- Discovery of edge cases not covered by unit tests
- Performance baseline establishment
- Bug identification with reproduction cases
**For the Knowledge Base:**
- Comprehensive testing documentation
- Real usage examples for documentation
- Edge case scenarios for future reference
- Performance insights and optimization opportunities
**For Development:**
- Priority list for bug fixes
- Enhancement ideas from real usage
- Validation of architectural decisions
- User experience insights
## Test Reporting
All test results will be captured in the Basic Memory test project, creating a living knowledge base of:
- Test execution logs with detailed observations
- Bug reports with reproduction steps
- Performance benchmarks and timing data
- Feature enhancement ideas discovered during testing
- Knowledge graphs showing test coverage relationships
- Summary reports for development team review
This approach ensures that the testing process itself validates Basic Memory's core value proposition: effectively capturing, organizing, and connecting knowledge through natural interaction patterns.
## Things to note
### User Experience & Usability:
- are tool instructions clear with working examples?
- Do error messages provide actionable guidance for resolution?
- Are response times acceptable for interactive use?
- Do tools feel consistent in their parameter patterns and behavior?
- Can users easily discover what tools are available and their capabilities?
### System Behavior:
- Does context preservation work as expected across tool calls?
- Do memory:// URLs behave intuitively for knowledge navigation?
- How well do tools work together in multi-step workflows?
- Does the system gracefully handle edge cases and invalid inputs?
### Documentation Alignment:
- does tool output provide clear results and helpful information?
- Do actual tool behaviors match their documented descriptions?
- Are the examples in tool help accurate and useful?
- Do real-world usage patterns align with documented workflows?
### Mental Model Validation:
- Does the system work the way users would naturally expect?
- Are there surprising behaviors that break user assumptions?
- Can users easily recover from mistakes or wrong turns?
- Do the knowledge graph concepts (entities, relations, observations) feel natural?
### Performance & Reliability:
- Do operations complete in reasonable time for the data size?
- Is system behavior consistent across multiple test sessions?
- How does performance change as the knowledge base grows?
- Are there any operations that feel unexpectedly slow?
---
**Ready to begin testing?** Start by creating the test project and recording your first observation about the testing setup process itself.
-1419
View File
File diff suppressed because it is too large Load Diff
-408
View File
@@ -1,408 +0,0 @@
---
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.
You can [download](https://github.com/basicmachines-co/basic-memory/blob/main/docs/AI%20Assistant%20Guide.md) the contents of this file from GitHub
# 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).
## Quick Reference
**Essential Tools:**
- `write_note()` - Create/update notes (primary tool)
- `read_note()` - Read existing content
- `search_notes()` - Find information
- `edit_note()` - Modify existing notes incrementally (v0.13.0)
- `move_note()` - Organize files with database consistency (v0.13.0)
**Project Management (v0.13.0):**
- `list_projects()` - Show available projects
- `switch_project()` - Change active project
- `get_current_project()` - Current project info
**Key Principles:**
1. **Build connections** - Rich knowledge graphs > isolated notes
2. **Ask permission** - "Would you like me to record this?"
3. **Use exact titles** - For accurate `[[WikiLinks]]`
4. **Leverage v0.13.0** - Edit incrementally, organize proactively, switch projects contextually
## Overview
Basic Memory allows you and users to record context in local Markdown files, building a rich knowledge base through natural conversations. The system automatically creates a semantic knowledge graph from simple text patterns.
- **Local-First**: All data is stored in plain text files on the user's computer
- **Real-Time**: Users see content updates immediately
- **Bi-Directional**: Both you and users can read and edit notes
- **Semantic**: Simple patterns create a structured knowledge graph
- **Persistent**: Knowledge persists across sessions and conversations
## The Importance of the Knowledge Graph
Basic Memory's value comes from connections between notes, not just the notes themselves. When writing notes, your primary goal should be creating a rich, interconnected knowledge graph.
When creating content, focus on:
1. **Increasing Semantic Density**: Add multiple observations and relations to each note
2. **Using Accurate References**: Aim to reference existing entities by their exact titles
3. **Creating Forward References**: Feel free to reference entities that don't exist yet - Basic Memory will resolve these when they're created later
4. **Creating Bidirectional Links**: When appropriate, connect entities from both directions
5. **Using Meaningful Categories**: Add semantic context with appropriate observation categories
6. **Choosing Precise Relations**: Use specific relation types that convey meaning
Remember that a knowledge graph with 10 heavily connected notes is more valuable than 20 isolated notes. Your job is to help build these connections.
## Core Tools Reference
### Essential Content Management
**Writing knowledge** (most important tool):
```
write_note(
title="Search Design",
content="# Search Design\n...",
folder="specs", # Optional
tags=["search", "design"], # v0.13.0: now searchable!
project="work-notes" # v0.13.0: target specific project
)
```
**Reading knowledge:**
```
read_note("Search Design") # By title
read_note("specs/search-design") # By path
read_note("memory://specs/search") # By memory URL
```
**Incremental editing** (v0.13.0):
```
edit_note(
identifier="Search Design",
operation="append", # append, prepend, find_replace, replace_section
content="\n## New Section\nContent here..."
)
```
**File organization** (v0.13.0):
```
move_note(
identifier="Old Note",
destination="archive/old-note.md" # Folders created automatically
)
```
### Project Management (v0.13.0)
```
list_projects() # Show available projects
switch_project("work-notes") # Change active project
get_current_project() # Current project info
```
### Search & Discovery
```
search_notes("authentication system") # v0.13.0: includes frontmatter tags
build_context("memory://specs/search") # Follow knowledge graph connections
recent_activity(timeframe="1 week") # Check what's been updated
```
## memory:// URLs Explained
Basic Memory uses a special URL format to reference entities in the knowledge graph:
- `memory://title` - Reference by title
- `memory://folder/title` - Reference by folder and title
- `memory://permalink` - Reference by permalink
- `memory://path/relation_type/*` - Follow all relations of a specific type
- `memory://path/*/target` - Find all entities with relations to target
## Semantic Markdown Format
Knowledge is encoded in standard markdown using simple patterns:
**Observations** - Facts about an entity:
```markdown
- [category] This is an observation #tag1 #tag2 (optional context)
```
**Relations** - Links between entities:
```markdown
- relation_type [[Target Entity]] (optional context)
```
**Common Categories & Relation Types:**
- Categories: `[idea]`, `[decision]`, `[question]`, `[fact]`, `[requirement]`, `[technique]`, `[recipe]`, `[preference]`
- Relations: `relates_to`, `implements`, `requires`, `extends`, `part_of`, `pairs_with`, `inspired_by`, `originated_from`
## When to Record Context
**Always consider recording context when**:
1. Users make decisions or reach conclusions
2. Important information emerges during conversation
3. Multiple related topics are discussed
4. The conversation contains information that might be useful later
5. Plans, tasks, or action items are mentioned
**Protocol for recording context**:
1. Identify valuable information in the conversation
2. Ask the user: "Would you like me to record our discussion about [topic] in Basic Memory?"
3. If they agree, use `write_note` to capture the information
4. If they decline, continue without recording
5. Let the user know when information has been recorded: "I've saved our discussion about [topic] to Basic Memory."
## Understanding User Interactions
Users will interact with Basic Memory in patterns like:
1. **Creating knowledge**:
```
Human: "Let's write up what we discussed about search."
You: I'll create a note capturing our discussion about the search functionality.
[Use write_note() to record the conversation details]
```
2. **Referencing existing knowledge**:
```
Human: "Take a look at memory://specs/search"
You: I'll examine that information.
[Use build_context() to gather related information]
[Then read_note() to access specific content]
```
3. **Finding information**:
```
Human: "What were our decisions about auth?"
You: Let me find that information for you.
[Use search_notes() to find relevant notes]
[Then build_context() to understand connections]
```
4. **Editing existing notes (v0.13.0)**:
```
Human: "Add a section about deployment to my API documentation"
You: I'll add that section to your existing documentation.
[Use edit_note() with operation="append" to add new content]
```
5. **Project management (v0.13.0)**:
```
Human: "Switch to my work project and show recent activity"
You: I'll switch to your work project and check what's been updated recently.
[Use switch_project() then recent_activity()]
```
6. **File organization (v0.13.0)**:
```
Human: "Move my old meeting notes to the archive folder"
You: I'll organize those notes for you.
[Use move_note() to relocate files with database consistency]
```
## Key Things to Remember
1. **Files are Truth**
- All knowledge lives in local files on the user's computer
- Users can edit files outside your interaction
- Changes need to be synced by the user (usually automatic)
- Always verify information is current with `recent_activity()`
2. **Building Context Effectively**
- Start with specific entities
- Follow meaningful relations
- Check recent changes
- Build context incrementally
- Combine related information
3. **Writing Knowledge Wisely**
- Same title+folder overwrites existing notes
- Structure with clear headings and semantic markup
- Use tags for searchability (v0.13.0: frontmatter tags indexed)
- Keep files organized in logical folders
4. **Leverage v0.13.0 Features**
- **Edit incrementally**: Use `edit_note()` for small changes vs rewriting
- **Switch projects**: Change context when user mentions different work areas
- **Organize proactively**: Move old content to archive folders
- **Cross-project operations**: Create notes in specific projects while maintaining context
## Common Knowledge Patterns
### Capturing Decisions
```markdown
---
title: Coffee Brewing Methods
tags: [coffee, brewing, pour-over, techniques] # v0.13.0: Now searchable!
---
# Coffee Brewing Methods
## Context
I've experimented with various brewing methods including French press, pour over, and espresso.
## Decision
Pour over is my preferred method for light to medium roasts because it highlights subtle flavors and offers more control over the extraction.
## Observations
- [technique] Blooming the coffee grounds for 30 seconds improves extraction #brewing
- [preference] Water temperature between 195-205°F works best #temperature
- [equipment] Gooseneck kettle provides better control of water flow #tools
- [timing] Total brew time of 3-4 minutes produces optimal extraction #process
## Relations
- pairs_with [[Light Roast Beans]]
- contrasts_with [[French Press Method]]
- requires [[Proper Grinding Technique]]
- part_of [[Morning Coffee Routine]]
```
### Recording Project Structure
```markdown
# Garden Planning
## Overview
This document outlines the garden layout and planting strategy for this season.
## Observations
- [structure] Raised beds in south corner for sun exposure #layout
- [structure] Drip irrigation system installed for efficiency #watering
- [pattern] Companion planting used to deter pests naturally #technique
## Relations
- contains [[Vegetable Section]]
- contains [[Herb Garden]]
- implements [[Organic Gardening Principles]]
```
### Technical Discussions
```markdown
# Recipe Improvement Discussion
## Key Points
Discussed strategies for improving the chocolate chip cookie recipe.
## Observations
- [issue] Cookies spread too thin when baked at 350°F #texture
- [solution] Chilling dough for 24 hours improves flavor and reduces spreading #technique
- [decision] Will use brown butter instead of regular butter #flavor
## Relations
- improves [[Basic Cookie Recipe]]
- inspired_by [[Bakery-Style Cookies]]
- pairs_with [[Homemade Ice Cream]]
```
## v0.13.0 Workflow Examples
### Multi-Project Conversations
**User:** "I need to update my work documentation and also add a personal recipe note."
**Workflow:**
1. `list_projects()` - Check available projects
2. `write_note(title="Sprint Planning", project="work-notes")` - Work content
3. `write_note(title="Weekend Recipes", project="personal")` - Personal content
### Incremental Note Building
**User:** "Add a troubleshooting section to my setup guide."
**Workflow:**
1. `edit_note(identifier="Setup Guide", operation="append", content="\n## Troubleshooting\n...")`
**User:** "Update the authentication section in my API docs."
**Workflow:**
1. `edit_note(identifier="API Documentation", operation="replace_section", section="## Authentication")`
### Smart File Organization
**User:** "My notes are getting messy in the main folder."
**Workflow:**
1. `move_note("Old Meeting Notes", "archive/2024/old-meetings.md")`
2. `move_note("Project Notes", "projects/client-work/notes.md")`
### Creating Effective Relations
When creating relations:
1. **Reference existing entities** by their exact title: `[[Exact Title]]`
2. **Create forward references** to entities that don't exist yet - they'll be linked automatically when created
3. **Search first** to find existing entities to reference
4. **Use meaningful relation types**: `implements`, `requires`, `part_of` vs generic `relates_to`
**Example workflow:**
1. `search_notes("travel")` to find existing travel-related notes
2. Reference found entities: `- part_of [[Japan Travel Guide]]`
3. Add forward references: `- located_in [[Tokyo]]` (even if Tokyo note doesn't exist yet)
## Common Issues & Solutions
**Missing Content:**
- Try `search_notes()` with broader terms if `read_note()` fails
- Use fuzzy matching: search for partial titles
**Forward References:**
- These are normal! Basic Memory links them automatically when target notes are created
- Inform users: "I've created forward references that will be linked when you create those notes"
**Sync Issues:**
- If information seems outdated, suggest `basic-memory sync`
- Use `recent_activity()` to check if content is current
## Best Practices
1. **Proactively Record Context**
- Offer to capture important discussions
- Record decisions, rationales, and conclusions
- Link to related topics
- Ask for permission first: "Would you like me to save our discussion about [topic]?"
- Confirm when complete: "I've saved our discussion to Basic Memory"
2. **Create a Rich Semantic Graph**
- **Add meaningful observations**: Include at least 3-5 categorized observations in each note
- **Create deliberate relations**: Connect each note to at least 2-3 related entities
- **Use existing entities**: Before creating a new relation, search for existing entities
- **Verify wikilinks**: When referencing `[[Entity]]`, use exact titles of existing notes
- **Check accuracy**: Use `search_notes()` or `recent_activity()` to confirm entity titles
- **Use precise relation types**: Choose specific relation types that convey meaning (e.g., "implements" instead of "relates_to")
- **Consider bidirectional relations**: When appropriate, create inverse relations in both entities
3. **Structure Content Thoughtfully**
- Use clear, descriptive titles
- Organize with logical sections (Context, Decision, Implementation, etc.)
- Include relevant context and background
- Add semantic observations with appropriate categories
- Use a consistent format for similar types of notes
- Balance detail with conciseness
4. **Navigate Knowledge Effectively**
- Start with specific searches
- Follow relation paths
- Combine information from multiple sources
- Verify information is current
- Build a complete picture before responding
5. **Help Users Maintain Their Knowledge**
- Suggest organizing related topics
- Identify potential duplicates
- Recommend adding relations between topics
- Offer to create summaries of scattered information
- Suggest potential missing relations: "I notice this might relate to [topic], would you like me to add that connection?"
Built with ♥️ by Basic Machines
-414
View File
@@ -1,414 +0,0 @@
---
title: CLI Reference
type: note
permalink: docs/cli-reference
---
# CLI Reference
Basic Memory provides command line tools for managing your knowledge base. This reference covers the available commands and their options.
## Core Commands
### auth (New in v0.13.0)
Manage OAuth authentication for secure remote access:
```bash
# Test authentication setup
basic-memory auth test-auth
# Register OAuth client
basic-memory auth register-client
```
Supports multiple authentication providers:
- **Basic Provider**: For development and testing
- **Supabase Provider**: For production deployments
- **External Providers**: GitHub, Google integration framework
See [[OAuth Authentication Guide]] for complete setup instructions.
### sync
Keeps files and the knowledge graph in sync:
```bash
# Basic sync
basic-memory sync
# Watch for changes
basic-memory sync --watch
# Show detailed sync information
basic-memory sync --verbose
```
Options:
- `--watch`: Continuously monitor for changes
- `--verbose`: Show detailed output
**Note**:
As of the v0.12.0 release syncing will occur in real time when the mcp process starts.
- The real time sync means that it is no longer necessary to run the `basic-memory sync --watch` process in a a terminal to sync changes to the db (so the AI can see them). This will be done automatically.
This behavior can be changed via the config. The config file for Basic Memory is in the home directory under `.basic-memory/config.json`.
To change the properties, set the following values:
```
~/.basic-memory/config.json
{
"sync_changes": false,
}
```
Thanks for using Basic Memory!
### import (Enhanced in v0.13.0)
Imports external knowledge sources with support for project targeting:
```bash
# Claude conversations
basic-memory import claude conversations
# Claude projects
basic-memory import claude projects
# ChatGPT history
basic-memory import chatgpt
# Memory JSON format
basic-memory import memory-json /path/to/memory.json
# Import to specific project (v0.13.0)
basic-memory --project=work import claude conversations
```
**New in v0.13.0:**
- **Project Targeting**: Import directly to specific projects
- **Real-time Sync**: Imported content available immediately
- **Unified Database**: All imports stored in centralized database
> **Note**: Changes sync automatically - no manual sync required in v0.13.0.
### status
Shows system status information:
```bash
# Basic status check
basic-memory status
# Detailed status
basic-memory status --verbose
# JSON output
basic-memory status --json
```
### project (Enhanced in v0.13.0)
Manage multiple projects with the new unified database architecture. Projects can now be switched instantly during conversations without restart.
```bash
# List all configured projects with status
basic-memory project list
# Create a new project
basic-memory project create work ~/work-basic-memory
# Set the default project
basic-memory project set-default work
# Delete a project (doesn't delete files)
basic-memory project delete personal
# Show detailed project statistics
basic-memory project info
```
**New in v0.13.0:**
- **Unified Database**: All projects share a single database for better performance
- **Instant Switching**: Switch projects during conversations without restart
- **Enhanced Commands**: Updated project commands with better status information
- **Project Statistics**: Detailed info about entities, observations, and relations
#### 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
```
### tool (Enhanced in v0.13.0)
Direct access to MCP tools via CLI with new editing and file management capabilities:
```bash
# Create notes
basic-memory tool write-note --title "My Note" --content "Content here"
# Edit notes incrementally (v0.13.0)
echo "New content" | basic-memory tool edit-note --title "My Note" --operation append
# Move notes (v0.13.0)
basic-memory tool move-note --identifier "My Note" --destination "archive/my-note.md"
# Search notes
basic-memory tool search-notes --query "authentication"
# Project management (v0.13.0)
basic-memory tool list-projects
basic-memory tool switch-project --project-name "work"
```
**New in v0.13.0:**
- **edit-note**: Incremental editing (append, prepend, find/replace, section replace)
- **move-note**: File management with database consistency
- **Project tools**: list-projects, switch-project, get-current-project
- **Cross-project operations**: Use `--project` flag with any tool
### help
The full list of commands and help for each can be viewed with the `--help` argument.
```
✗ basic-memory --help
Usage: basic-memory [OPTIONS] COMMAND [ARGS]...
Basic Memory - Local-first personal knowledge management system.
╭─ Options ─────────────────────────────────────────────────────────────────────────────────╮
│ --project -p TEXT Specify which project to use │
│ [env var: BASIC_MEMORY_PROJECT] │
│ [default: None] │
│ --version -V Show version information and exit. │
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to copy it or │
│ customize the installation. │
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────────────────╮
│ auth OAuth authentication management (v0.13.0) │
│ sync Sync knowledge files with the database │
│ status Show sync status between files and database │
│ reset Reset database (drop all tables and recreate) │
│ mcp Run the MCP server for Claude Desktop integration │
│ import Import data from various sources │
│ tool Direct access to MCP tools via CLI │
│ project Manage multiple Basic Memory projects │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
```
## Initial Setup
```bash
# Install Basic Memory
uv install basic-memory
# First sync
basic-memory sync
# Start watching mode
basic-memory sync --watch
```
> **Important**: You need to install Basic Memory via `uv` or `pip` to use the command line tools, see [[Getting Started with Basic Memory#Installation]].
## Regular Usage
```bash
# Check status
basic-memory status
# Import new content
basic-memory import claude conversations
# Sync changes
basic-memory sync
# Sync changes continuously
basic-memory sync --watch
```
## Maintenance Tasks
```bash
# Check system status in detail
basic-memory status --verbose
# Full resync of all files
basic-memory sync
# Import updates to specific folder
basic-memory import claude conversations --folder new
```
## Using stdin with Basic Memory's `write_note` Tool
The `write-note` tool supports reading content from standard input (stdin), allowing for more flexible workflows when creating or updating notes in your Basic Memory knowledge base.
### Use Cases
This feature is particularly useful for:
1. **Piping output from other commands** directly into Basic Memory notes
2. **Creating notes with multi-line content** without having to escape quotes or special characters
3. **Integrating with AI assistants** like Claude Code that can generate content and pipe it to Basic Memory
4. **Processing text data** from files or other sources
### Basic Usage
#### Method 1: Using a Pipe
You can pipe content from another command into `write_note`:
```bash
# Pipe output of a command into a new note
echo "# My Note\n\nThis is a test note" | basic-memory tool write-note --title "Test Note" --folder "notes"
# Pipe output of a file into a new note
cat README.md | basic-memory tool write-note --title "Project README" --folder "documentation"
# Process text through other tools before saving as a note
cat data.txt | grep "important" | basic-memory tool write-note --title "Important Data" --folder "data"
```
#### Method 2: Using Heredoc Syntax
For multi-line content, you can use heredoc syntax:
```bash
# Create a note with heredoc
cat << EOF | basic-memory tool write_note --title "Project Ideas" --folder "projects"
# Project Ideas for Q2
## AI Integration
- Improve recommendation engine
- Add semantic search to product catalog
## Infrastructure
- Migrate to Kubernetes
- Implement CI/CD pipeline
EOF
```
#### Method 3: Input Redirection
You can redirect input from a file:
```bash
# Create a note from file content
basic-memory tool write-note --title "Meeting Notes" --folder "meetings" < meeting_notes.md
```
## Integration with Claude Code
This feature works well with Claude Code in the terminal:
### cli
In a Claude Code session, let Claude know he can use the basic-memory tools, then he can execute them via the cli:
```
⏺ Bash(echo "# Test Note from Claude\n\nThis is a test note created by Claude to test the stdin functionality." | basic-memory tool write-note --title "Claude Test Note" --folder "test" --tags "test" --tags "claude")…
  ⎿  # Created test/Claude Test Note.md (23e00eec)
permalink: test/claude-test-note
## Tags
- test, claude
```
### MCP
Claude code can also now use mcp tools, so it can use any of the basic-memory tool natively. To install basic-memory in Claude Code:
Run
```
claude mcp add basic-memory basic-memory mcp
```
For example:
```
➜ ~ claude mcp add basic-memory basic-memory mcp
Added stdio MCP server basic-memory with command: basic-memory mcp to project config
➜ ~ claude mcp list
basic-memory: basic-memory mcp
```
You can then use the `/mcp` command in the REPL:
```
/mcp
⎿ MCP Server Status
• basic-memory: connected
```
## Version Management (New in v0.13.0)
Basic Memory v0.13.0 introduces automatic version management and multiple installation options:
```bash
# Stable releases
pip install basic-memory
# Beta/pre-releases
pip install basic-memory --pre
# Latest development builds (auto-published)
pip install basic-memory --pre --force-reinstall
# Check current version
basic-memory --version
```
**Version Types:**
- **Stable**: `0.13.0` (manual git tags)
- **Beta**: `0.13.0b1` (manual git tags)
- **Development**: `0.12.4.dev26+468a22f` (automatic from commits)
## Troubleshooting Common Issues
### Sync Conflicts
If you encounter a file changed during sync error:
1. Check the file referenced in the error message
2. Resolve any conflicts manually
3. Run sync again
### Import Errors
If import fails:
1. Check that the source file is in the correct format
2. Verify permissions on the target directory
3. Use --verbose flag for detailed error information
### Status Issues
If status shows problems:
1. Note any unresolved relations or warnings
2. Run a full sync to attempt automatic resolution
3. Check file permissions if database access errors occur
## Relations
- used_by [[Getting Started with Basic Memory]] (Installation instructions)
- complements [[User Guide]] (How to use Basic Memory)
- relates_to [[Introduction to Basic Memory]] (System overview)
-107
View File
@@ -1,107 +0,0 @@
---
title: Canvas Visualizations
type: note
permalink: docs/canvas
tags:
- visualization
- mapping
- obsidian
---
# Canvas Visualizations
Basic Memory can create visual knowledge maps using Obsidian's Canvas feature. These visualizations help you understand relationships between concepts, map out processes, and visualize your knowledge structure.
## Creating Canvas Visualizations
Ask Claude to create a visualization by describing what you want to map:
```
You: "Create a canvas visualization of my project components and their relationships."
You: "Make a concept map showing the main themes from our discussion about climate change."
You: "Can you make a canvas diagram of the perfect pour over method?"
```
![[Canvas.png]]
## Types of Visualizations
Basic Memory can create several types of visual maps:
### Document Maps
Visualize connections between your notes and documents
### Concept Maps
Create visual representations of ideas and their relationships
### Process Diagrams
Map workflows, sequences, and procedures
### Thematic Analysis
Organize ideas around central themes
### Relationship Networks
Show how different entities relate to each other
## Visualization Sources
Claude can create visualizations based on:
### Documents in Your Knowledge Base
```
You: "Create a canvas showing the connections between my project planning documents"
```
### Conversation Content
```
You: "Make a canvas visualization of the main points we just discussed"
```
### Search Results
```
You: "Find all my notes about psychology and create a visual map of the concepts"
```
### Themes and Relationships
```
You: "Create a visual map showing how different philosophical schools relate to each other"
```
## Visualization Workflow
1. **Request a visualization** by describing what you want to see
2. **Claude creates the canvas file** in your Basic Memory directory
3. **Open the file in Obsidian** to view the visualization
4. **Refine the visualization** by asking Claude for adjustments:
```
You: "Could you reorganize the canvas to group related components together?"
You: "Please add more detail about the connection between these two concepts."
```
## Technical Details
Behind the scenes, Claude:
1. Creates a `.canvas` file in JSON format
2. Adds nodes for each concept or document
3. Creates edges to represent relationships
4. Sets positions for visual clarity
5. Includes any relevant metadata
The resulting file is fully compatible with Obsidian's Canvas feature and can be edited directly in Obsidian.
## Tips for Effective Visualizations
- **Be specific** about what you want to visualize
- **Specify the level of detail** you need
- **Mention the visualization type** you want (concept map, process flow, etc.)
- **Start simple** and ask for refinements
- **Provide context** about what documents or concepts to include
## Relations
- enhances [[Obsidian Integration]] (Using Basic Memory with Obsidian)
- visualizes [[Knowledge Format]] (The structure of your knowledge)
- complements [[User Guide]] (Ways to use Basic Memory)
-335
View File
@@ -1,335 +0,0 @@
# Claude.ai Integration Guide
This guide explains how to connect Basic Memory to Claude.ai, enabling Claude to read and write to your personal knowledge base.
## Overview
When connected to Claude.ai, Basic Memory provides:
- Persistent memory across conversations
- Knowledge graph navigation
- Note-taking and search capabilities
- File organization and management
## Prerequisites
1. Basic Memory MCP server with OAuth enabled
2. Public HTTPS URL (or tunneling service for testing)
3. Claude.ai account (Free, Pro, or Enterprise)
## Quick Start (Testing)
### 1. Start MCP Server with OAuth
```bash
# Enable OAuth with basic provider
export FASTMCP_AUTH_ENABLED=true
export FASTMCP_AUTH_PROVIDER=basic
# Start server on all interfaces
basic-memory mcp --transport streamable-http --host 0.0.0.0 --port 8000
```
### 2. Make Server Accessible
For testing, use ngrok:
```bash
# Install ngrok
brew install ngrok # macOS
# or download from https://ngrok.com
# Create tunnel
ngrok http 8000
```
Note the HTTPS URL (e.g., `https://abc123.ngrok.io`)
### 3. Register OAuth Client
```bash
# Register a client for Claude
basic-memory auth register-client --client-id claude-ai
# Save the credentials!
# Client ID: claude-ai
# Client Secret: xxx...
```
### 4. Connect in Claude.ai
1. Go to Claude.ai → Settings → Integrations
2. Click "Add More"
3. Enter your server URL: `https://abc123.ngrok.io/mcp`
4. Click "Connect"
5. Authorize the connection
### 5. Use in Conversations
- Click the tools icon (🔧) in the chat
- Select "Basic Memory"
- Try commands like:
- "Create a note about our meeting"
- "Search for project ideas"
- "Show recent notes"
## Production Setup
### 1. Deploy with Supabase Auth
```bash
# .env file
FASTMCP_AUTH_ENABLED=true
FASTMCP_AUTH_PROVIDER=supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-key
```
### 2. Deploy to Cloud
Options for deployment:
#### Vercel
```json
// vercel.json
{
"functions": {
"api/mcp.py": {
"runtime": "python3.9"
}
}
}
```
#### Railway
```bash
# Install Railway CLI
brew install railway
# Deploy
railway init
railway up
```
#### Docker
```dockerfile
FROM python:3.12
WORKDIR /app
COPY . .
RUN pip install -e .
CMD ["basic-memory", "mcp", "--transport", "streamable-http"]
```
### 3. Configure for Organization
For Claude.ai Enterprise:
1. **Admin Setup**:
- Go to Organizational Settings
- Navigate to Integrations
- Add MCP server URL for all users
- Configure allowed scopes
2. **User Permissions**:
- Users connect individually
- Each user has their own auth token
- Scopes determine access level
## Security Best Practices
### 1. Use HTTPS
- Required for OAuth
- Encrypt all data in transit
- Use proper SSL certificates
### 2. Implement Scopes
```bash
# Configure required scopes
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
# User-specific scopes
read: Can search and read notes
write: Can create and update notes
admin: Can manage all data
```
### 3. Token Security
- Short-lived access tokens (1 hour)
- Refresh token rotation
- Secure token storage
### 4. Rate Limiting
```python
# In your MCP server
from fastapi import HTTPException
from slowapi import Limiter
limiter = Limiter(key_func=get_remote_address)
@app.get("/mcp")
@limiter.limit("100/minute")
async def mcp_endpoint():
# Handle MCP requests
```
## Advanced Features
### 1. Custom Tools
Create specialized tools for Claude:
```python
@mcp.tool()
async def analyze_notes(topic: str) -> str:
"""Analyze all notes on a specific topic."""
# Search and analyze implementation
return analysis
```
### 2. Context Preservation
Use memory:// URLs to maintain context:
```python
@mcp.tool()
async def continue_conversation(memory_url: str) -> str:
"""Continue from a previous conversation."""
context = await build_context(memory_url)
return context
```
### 3. Multi-User Support
With Supabase, each user has isolated data:
```sql
-- Row-level security
CREATE POLICY "Users see own notes"
ON notes FOR SELECT
USING (auth.uid() = user_id);
```
## Troubleshooting
### Connection Issues
1. **"Failed to connect"**
- Verify server is running
- Check HTTPS is working
- Confirm OAuth is enabled
2. **"Authorization failed"**
- Check client credentials
- Verify redirect URLs
- Review OAuth logs
3. **"No tools available"**
- Ensure MCP tools are registered
- Check required scopes
- Verify transport type
### Debug Mode
Enable detailed logging:
```bash
# Server side
export FASTMCP_LOG_LEVEL=DEBUG
export LOGURU_LEVEL=DEBUG
# Check logs
tail -f logs/mcp.log
```
### Test Connection
```bash
# Test OAuth flow
curl https://your-server.com/mcp/.well-known/oauth-authorization-server
# Should return OAuth metadata
{
"issuer": "https://your-server.com",
"authorization_endpoint": "https://your-server.com/auth/authorize",
...
}
```
## Best Practices
1. **Regular Backups**
- Export your knowledge base
- Use version control
- Multiple storage locations
2. **Access Control**
- Principle of least privilege
- Regular token rotation
- Audit access logs
3. **Performance**
- Index frequently searched fields
- Optimize large knowledge bases
- Use caching where appropriate
4. **User Experience**
- Clear tool descriptions
- Helpful error messages
- Quick response times
## Examples
### Creating Notes
```
User: Create a note about the meeting with the product team
Claude: I'll create a note about your meeting with the product team.
[Uses write_note tool]
Note created: "Meeting with Product Team - 2024-01-15"
Location: Work/Meetings/
I've documented the meeting notes. The note includes the date, attendees, and key discussion points.
```
### Searching Knowledge
```
User: What did we discuss about the API redesign?
Claude: Let me search for information about the API redesign.
[Uses search_notes tool]
I found 3 relevant notes about the API redesign:
1. "API Redesign Proposal" (2024-01-10)
- RESTful architecture
- Version 2.0 specifications
- Migration timeline
2. "Technical Review: API Changes" (2024-01-12)
- Breaking changes documented
- Backwards compatibility plan
3. "Meeting: API Implementation" (2024-01-14)
- Team assignments
- Q1 deliverables
```
## Next Steps
1. Set up production deployment
2. Configure organizational access
3. Create custom tools for your workflow
4. Implement advanced security features
5. Monitor usage and performance
## Resources
- [Basic Memory Documentation](../README.md)
- [OAuth Setup Guide](OAuth%20Authentication.md)
- [MCP Specification](https://modelcontextprotocol.io)
- [Claude.ai Help Center](https://support.anthropic.com)
-355
View File
@@ -1,355 +0,0 @@
---
title: Getting Started with Basic Memory
type: note
permalink: docs/getting-started
---
# Getting Started with Basic Memory
This guide will help you install Basic Memory, configure it with Claude Desktop, and create your first knowledge notes
through conversations.
Basic Memory uses the [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) to connect with LLMs.
It can be used with any service that supports the MCP, but Claude Desktop works especially well.
## Installation
### Prerequisites
The easiest way to install basic memory is via `uv`. See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/).
### 1. Install Basic Memory
**v0.13.0 offers multiple installation options:**
```bash
# Stable release (recommended)
uv tool install basic-memory
# or: pip install basic-memory
# Beta releases (new features, testing)
pip install basic-memory --pre
# Development builds (latest changes)
pip install basic-memory --pre --force-reinstall
```
**Version Information:**
- **Stable**: Latest tested release (e.g., `0.13.0`)
- **Beta**: Pre-release versions (e.g., `0.13.0b1`)
- **Development**: Auto-published from git commits (e.g., `0.12.4.dev26+468a22f`)
> **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
Edit your Claude Desktop config, located at `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": [
"basic-memory",
"mcp"
]
}
}
}
```
**Restart Claude Desktop**. You should see Basic Memory tools available in the "tools" menu in Claude Desktop (the little hammer icon in the bottom-right corner of the chat interface). Click it to view available tools.
#### Fix Path to uv
If you get an error that says `ENOENT` , this most likely means Claude Desktop could not find your `uv` installation. Make sure that you have `uv` installed per the instructions above, then:
**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 Claude Desktop config:
```json
{
"mcpServers": {
"basic-memory": {
"command": "/absolute/path/to/uvx",
"args": [
"basic-memory",
"mcp"
]
}
}
}
```
Replace `/absolute/path/to/uvx` with the actual path you found in Step 1.
**Step 3: Restart Claude Desktop**
Close and reopen Claude Desktop for the changes to take effect.
### 3. Sync changes in real time
> **Note**: The service will sync changes from your project directory in real time so they available for the AI assistant.
To disable realtime sync, you can update the config. See [[CLI Reference#sync]].
### 4. Staying Updated
To update Basic Memory when new versions are released:
```bash
# Update stable release
uv tool upgrade basic-memory
# or: pip install --upgrade basic-memory
# Update to latest beta (v0.13.0)
pip install --upgrade basic-memory --pre
# Get latest development build
pip install --upgrade basic-memory --pre --force-reinstall
```
**v0.13.0 Update Benefits:**
- **Fluid project switching** during conversations
- **Advanced note editing** capabilities
- **Smart file management** with move operations
- **Enhanced search** with frontmatter tag support
> **Note**: After updating, restart Claude Desktop for changes to take effect. No sync restart needed in v0.13.0.
### 5. Multi-Project Setup (Enhanced in v0.13.0)
By default, Basic Memory creates a project in `~/basic-memory`. v0.13.0 introduces **fluid project management** - switch between projects instantly during conversations.
```
# Create a new project
basic-memory project create work ~/work-basic-memory
# Set the default project
basic-memory project set-default work
# List all projects with status
basic-memory project list
# Get detailed project information
basic-memory project info
```
**New in v0.13.0:**
- **Instant switching**: Change projects during conversations without restart
- **Unified database**: All projects in single `~/.basic-memory/memory.db`
- **Better performance**: Optimized queries and reduced file I/O
- **Session context**: Maintains active project throughout conversations
## 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**: You can view the sync status by running `basic-memory status
.
#### 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. **Open Claude Desktop** and start a new conversation.
2. **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..."
```
3. **Ask Claude to create a note**:
```
You: "Could you create a note summarizing what we've discussed about coffee brewing?"
```
4. **Confirm note creation**:
Claude will confirm when the note has been created and where it's stored.
5. **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
tags: [coffee, brewing, equipment] # v0.13.0: Now searchable!
---
# Coffee Brewing Methods
## Observations
- [method] Pour over provides more clarity...
- [technique] Water temperature at 205°F...
## Relations
- relates_to [[Other Coffee Topics]]
```
**v0.13.0 Improvements:**
- **Real-time sync**: Changes appear immediately, no background sync needed
- **Searchable tags**: Frontmatter tags are now indexed for search
- **Better file organization**: Enhanced file management capabilities
## Using Special Prompts
Basic Memory includes special prompts that help you start conversations with context from your knowledge base:
### Continue Conversation
To resume a previous topic:
```
You: "Let's continue our conversation about coffee brewing."
```
This prompt triggers Claude to:
1. Search your knowledge base for relevant content about coffee brewing
2. Build context from these documents
3. Resume the conversation with full awareness of previous discussions
### Recent Activity
To see what you've been working on:
```
You: "What have we been discussing recently?"
```
This prompt causes Claude to:
1. Retrieve documents modified in the recent past
2. Summarize the topics and main points
3. Offer to continue any of those discussions
### Search
To find specific information:
```
You: "Find information about pour over coffee methods."
```
Claude will:
1. Search your knowledge base for relevant documents
2. Summarize the key findings
3. Offer to explore specific documents in more detail
See [[User Guide#Using Special Prompts]] for further information.
## Using Your Knowledge Base
### Referencing Knowledge
In future conversations, reference your existing knowledge:
```
You: "What water temperature did we decide was optimal for coffee brewing?"
```
Or directly reference notes using memory:// URLs:
```
You: "Take a look at memory://coffee-brewing-methods and let's discuss how to improve my technique."
```
### Building On Previous Knowledge (Enhanced in v0.13.0)
Basic Memory enables continuous knowledge building:
1. **Reference previous discussions** in new conversations
2. **Edit notes incrementally** without rewriting entire documents
3. **Move and organize notes** as your knowledge base grows
4. **Switch between projects** instantly during conversations
5. **Search by tags** to find related content quickly
6. **Create connections** between related topics
7. **Follow relationships** to build comprehensive context
### v0.13.0 Workflow Examples
**Incremental Editing:**
```
You: "Add a section about espresso to my coffee brewing notes"
Claude: [Uses edit_note to append new section]
```
**File Organization:**
```
You: "Move my old meeting notes to an archive folder"
Claude: [Uses move_note with database consistency]
```
**Project Switching:**
```
You: "Switch to my work project and show recent activity"
Claude: [Switches projects and shows work-specific content]
```
## Importing Existing Conversations
Import your existing AI conversations:
```bash
# From Claude
basic-memory import claude conversations
# From ChatGPT
basic-memory import chatgpt
```
After importing, changes sync automatically in real-time. You can see project statistics by running `basic-memory project info`.
## Quick Tips
### General Usage
- Basic Memory syncs changes in real-time (no manual sync needed)
- Use special prompts (Continue Conversation, Recent Activity, Search) to start contextual discussions
- Build connections between notes for a richer knowledge graph
- Use direct `memory://` URLs with permalinks for precise context
- Review and edit AI-generated notes for accuracy
### v0.13.0 Features
- **Switch projects instantly**: "Switch to my work project" - no restart needed
- **Edit notes incrementally**: "Add a section about..." instead of rewriting
- **Organize with moves**: "Move this to my archive folder" with database consistency
- **Search by tags**: Frontmatter tags are now searchable
- **Try beta builds**: `pip install basic-memory --pre` for latest features
## Next Steps
After getting started, explore these areas:
1. **Read the [[User Guide]]** for comprehensive usage instructions
2. **Understand the [[Knowledge Format]]** to learn how knowledge is structured
3. **Set up [[Obsidian Integration]]** for visual knowledge navigation
4. **Learn about [[Canvas]]** visualizations for mapping concepts
5. **Review the [[CLI Reference]]** for command line tools
6. **Explore [[OAuth Authentication Guide]]** for secure remote access (v0.13.0)
7. **Set up multiple projects** for different knowledge areas (v0.13.0)
-207
View File
@@ -1,207 +0,0 @@
---
title: Knowledge Format
type: note
permalink: docs/knowledge-format
tags:
- architecture
- patterns
- knowledge
- design
---
# Knowledge Format
Basic Memory uses standard Markdown with simple semantic patterns to create a knowledge graph. This document details the file structure and patterns used to organize knowledge.
## File-First Architecture
All knowledge in Basic Memory is stored in plain text Markdown files:
- Files are the source of truth for all knowledge
- Changes to files automatically update the knowledge graph
- You maintain complete ownership and control
- Files work with git and other version control systems
- Knowledge persists independently of any AI conversation
## Core Document Structure
Every document uses this basic structure:
```markdown
---
title: Document Title
type: note
tags: [tag1, tag2]
permalink: custom-path
---
# Document Title
Regular markdown content...
## Observations
- [category] Content with #tags (optional context)
## Relations
- relation_type [[Other Document]] (optional context)
```
### Frontmatter
The YAML frontmatter at the top of each file defines essential metadata:
```yaml
---
title: Document Title # Used for linking and references
type: note # Document type
tags: [tag1, tag2] # For organization and searching
permalink: custom-link # Optional custom URL path
---
```
The title is particularly important as it's used to create links between documents.
### Observations
Observations are facts or statements about a topic:
```markdown
## Observations
- [tech] Uses SQLite for storage #database
- [design] Follows local-first architecture #architecture
- [decision] Selected bcrypt for passwords #security (Based on audit)
```
Each observation contains:
- **Category** in [brackets] - classifies the information type
- **Content text** - the main information
- Optional **#tags** - additional categorization
- Optional **(context)** - supporting details
Common categories include:
- `[tech]`: Technical details
- `[design]`: Architecture decisions
- `[feature]`: User capabilities
- `[decision]`: Choices that were made
- `[principle]`: Fundamental concepts
- `[method]`: Approaches or techniques
- `[preference]`: Personal opinions
### Relations
Relations connect documents to form the knowledge graph:
```markdown
## Relations
- implements [[Search Design]]
- depends_on [[Database Schema]]
- relates_to [[User Interface]]
```
You can also create inline references:
```markdown
This builds on [[Core Design]] and uses [[Utility Functions]].
```
Common relation types include:
- `implements`: Implementation of a specification
- `depends_on`: Required dependency
- `relates_to`: General connection
- `inspired_by`: Source of ideas
- `extends`: Enhancement
- `part_of`: Component relationship
- `contains`: Hierarchical relationship
- `pairs_with`: Complementary relationship
## Knowledge Graph
Basic Memory automatically builds a knowledge graph from your document connections:
- Each document becomes a node in the graph
- Relations create edges between nodes
- Relation types add semantic meaning to connections
- Forward references can link to documents that don't exist yet
This graph enables rich context building and navigation across your knowledge base.
## Permalinks and memory:// URLs
Every document in Basic Memory has a unique permalink that serves as its stable identifier:
### How Permalinks Work
- **Automatically assigned**: The system generates a permalink for each document
- **Based on title**: By default, derived from the document title
- **Always unique**: If conflicts exist, the system adds a suffix to ensure uniqueness
- **Stable reference**: Remains the same even if the file moves in the directory structure
- **Used in memory:// URLs**: Forms the basis of the memory:// addressing scheme
You can specify a custom permalink in the frontmatter:
```yaml
---
title: Authentication Approaches
permalink: auth-approaches-2024
---
```
If not specified, one will be generated automatically from the title, if the note has has a frontmatter section.
By default a notes' permalink value will not change if the file is moved. It's a **stable** identifier :). But if you'd rather permalinks are always updated when a file moves, you can set the config setting in the global config.
The config file for Basic Memory is in the home directory under `.basic-memory/config.json`.
To change the behavior, set the following value:
```
~/.basic-memory/config.json
{
"update_permalinks_on_move": true
}
```
### Using memory:// URLs
The memory:// URL scheme provides a reliable way to reference knowledge:
```
memory://auth-approaches-2024 # Direct access by permalink
memory://Authentication Approaches # Access by title (automatically resolves)
memory://project/auth-approaches # Access by path
```
Memory URLs support pattern matching for more powerful queries:
```
memory://auth* # All documents with permalinks starting with "auth"
memory://*/approaches # All documents with permalinks ending with "approaches"
memory://project/*/requirements # All requirements documents in the project folder
memory://docs/search/implements/* # Follow all implements relations from search docs
```
This addressing scheme ensures content remains accessible even as your knowledge base evolves and files are reorganized.
## File Organization
Organize files in any structure that suits your needs:
```
docs/
architecture/
design.md
patterns.md
features/
search.md
auth.md
```
You can:
- Group by topic in folders
- Use a flat structure with descriptive filenames
- Tag files for easier discovery
- Add custom metadata in frontmatter
The system will build the semantic knowledge graph regardless of how you organize your files.
## Relations
- implemented_by [[User Guide]] (How to work with this format)
- relates_to [[Getting Started with Basic Memory]] (Setup instructions)
- explained_in [[Introduction to Basic Memory]] (Overview of the system)
-259
View File
@@ -1,259 +0,0 @@
# OAuth Authentication Guide
Basic Memory MCP server supports OAuth 2.1 authentication for secure access control. This guide covers setup, testing, and production deployment.
## Quick Start
### 1. Enable OAuth
```bash
# Set environment variable
export FASTMCP_AUTH_ENABLED=true
# Or use .env file
echo "FASTMCP_AUTH_ENABLED=true" >> .env
```
### 2. Start the Server
```bash
basic-memory mcp --transport streamable-http
```
### 3. Test with MCP Inspector
Since the basic auth provider uses in-memory storage with per-instance secret keys, you'll need to use a consistent approach:
#### Option A: Use Environment Variable for Secret Key
```bash
# Set a fixed secret key for testing
export FASTMCP_AUTH_SECRET_KEY="your-test-secret-key"
# Start the server
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
# In another terminal, register a client
basic-memory auth register-client --client-id=test-client
# Get a token using the same secret key
basic-memory auth test-auth
```
#### Option B: Use the Built-in Test Endpoint
```bash
# Start server with OAuth
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
# Register a client and get token in one step
curl -X POST http://localhost:8000/register \
-H "Content-Type: application/json" \
-d '{"client_metadata": {"client_name": "Test Client"}}'
# Use the returned client_id and client_secret
curl -X POST http://localhost:8000/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
```
### 4. Configure MCP Inspector
1. Open MCP Inspector
2. Configure:
- Server URL: `http://localhost:8000/mcp/` (note the trailing slash!)
- Transport: `streamable-http`
- Custom Headers:
```
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json, text/event-stream
```
## OAuth Endpoints
The server provides these OAuth endpoints automatically:
- `GET /authorize` - Authorization endpoint
- `POST /token` - Token exchange endpoint
- `GET /.well-known/oauth-authorization-server` - OAuth metadata
- `POST /register` - Client registration (if enabled)
- `POST /revoke` - Token revocation (if enabled)
## OAuth Flow
### Standard Authorization Code Flow
1. **Get Authorization Code**:
```bash
curl "http://localhost:8000/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8000/callback&response_type=code&code_challenge=YOUR_CHALLENGE&code_challenge_method=S256"
```
2. **Exchange Code for Token**:
```bash
curl -X POST http://localhost:8000/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=AUTH_CODE&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&code_verifier=YOUR_VERIFIER"
```
3. **Use Access Token**:
```bash
curl http://localhost:8000/mcp \
-H "Authorization: Bearer ACCESS_TOKEN"
```
## Production Deployment
### Using Supabase Auth
For production, use Supabase for persistent auth storage:
```bash
# Configure environment
FASTMCP_AUTH_ENABLED=true
FASTMCP_AUTH_PROVIDER=supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-key
# Start server
basic-memory mcp --transport streamable-http --host 0.0.0.0
```
### Security Requirements
1. **HTTPS Required**: OAuth requires HTTPS in production (localhost exception for testing)
2. **PKCE Support**: Claude.ai requires PKCE for authorization
3. **Token Expiration**: Access tokens expire after 1 hour
4. **Scopes**: Supported scopes are `read`, `write`, and `admin`
## Connecting from Claude.ai
1. **Deploy with HTTPS**:
```bash
# Use ngrok for testing
ngrok http 8000
# Or deploy to cloud provider
```
2. **Configure in Claude.ai**:
- Go to Settings → Integrations
- Click "Add More"
- Enter: `https://your-server.com/mcp`
- Click "Connect"
- Authorize in the popup window
## Debugging
### Common Issues
1. **401 Unauthorized**:
- Check token is valid and not expired
- Verify secret key consistency
- Ensure bearer token format: `Authorization: Bearer TOKEN`
2. **404 on Auth Endpoints**:
- Endpoints are at root, not under `/auth`
- Use `/authorize` not `/auth/authorize`
3. **Token Validation Fails**:
- Basic provider uses in-memory storage
- Tokens don't persist across server restarts
- Use same secret key for testing
### Debug Commands
```bash
# Check OAuth metadata
curl http://localhost:8000/.well-known/oauth-authorization-server
# Enable debug logging
export FASTMCP_LOG_LEVEL=DEBUG
# Test token directly
curl http://localhost:8000/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-v
```
## Provider Options
- **basic**: In-memory storage (development only)
- **supabase**: Recommended for production
- **github**: GitHub OAuth integration
- **google**: Google OAuth integration
## Example Test Script
```python
import httpx
import asyncio
from urllib.parse import urlparse, parse_qs
async def test_oauth_flow():
"""Test the full OAuth flow"""
client_id = "test-client"
client_secret = "test-secret"
async with httpx.AsyncClient() as client:
# 1. Get authorization code
auth_response = await client.get(
"http://localhost:8000/authorize",
params={
"client_id": client_id,
"redirect_uri": "http://localhost:8000/callback",
"response_type": "code",
"code_challenge": "test-challenge",
"code_challenge_method": "S256",
"state": "test-state"
}
)
# Extract code from redirect URL
redirect_url = auth_response.headers.get("Location")
parsed = urlparse(redirect_url)
code = parse_qs(parsed.query)["code"][0]
# 2. Exchange for token
token_response = await client.post(
"http://localhost:8000/token",
data={
"grant_type": "authorization_code",
"code": code,
"client_id": client_id,
"client_secret": client_secret,
"code_verifier": "test-verifier",
"redirect_uri": "http://localhost:8000/callback"
}
)
tokens = token_response.json()
print(f"Access token: {tokens['access_token']}")
# 3. Test MCP endpoint
mcp_response = await client.post(
"http://localhost:8000/mcp",
headers={"Authorization": f"Bearer {tokens['access_token']}"},
json={"method": "initialize", "params": {}}
)
print(f"MCP Response: {mcp_response.status_code}")
asyncio.run(test_oauth_flow())
```
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `FASTMCP_AUTH_ENABLED` | Enable OAuth authentication | `false` |
| `FASTMCP_AUTH_PROVIDER` | OAuth provider type | `basic` |
| `FASTMCP_AUTH_SECRET_KEY` | JWT signing key (basic provider) | Random |
| `FASTMCP_AUTH_ISSUER_URL` | OAuth issuer URL | `http://localhost:8000` |
| `FASTMCP_AUTH_REQUIRED_SCOPES` | Required scopes (comma-separated) | `read,write` |
## Next Steps
- [Supabase OAuth Setup](./Supabase%20OAuth%20Setup.md) - Production auth setup
- [External OAuth Providers](./External%20OAuth%20Providers.md) - GitHub, Google integration
- [MCP OAuth Specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) - Official spec
-146
View File
@@ -1,146 +0,0 @@
---
title: Obsidian Integration
type: note
permalink: docs/obsidian-integration
---
# Obsidian Integration
Basic Memory integrates seamlessly with [Obsidian](https://obsidian.md), providing powerful visualization and navigation capabilities for your knowledge graph.
## Setup
### Creating an Obsidian Vault
1. Download and install [Obsidian](https://obsidian.md)
2. Create a new vault
3. Point it to your Basic Memory directory (~/basic-memory by default)
4. Enable core plugins like Graph View, Backlinks, and Tags
## Visualization Features
### Graph View
Obsidian's Graph View provides a visual representation of your knowledge network:
- Each document appears as a node
- Relations appear as connections between nodes
- Colors can be customized to distinguish types
- Filters let you focus on specific aspects
- Local graphs show connections for individual documents
### Backlinks
Obsidian automatically tracks references between documents:
- View all documents that reference the current one
- See the exact context of each reference
- Navigate easily through connections
- Track how concepts relate to each other
### Tag Explorer
Use tags to organize and filter content:
- View all tags in your knowledge base
- See how many documents use each tag
- Filter documents by tag combinations
- Create hierarchical tag structures
## Knowledge Elements
Basic Memory's knowledge format works natively with Obsidian:
### Wiki Links
```markdown
## Relations
- implements [[Search Design]]
- depends_on [[Database Schema]]
```
These display as clickable links in Obsidian and appear in the graph view.
### Observations with Tags
```markdown
## Observations
- [tech] Using SQLite #database
- [design] Local-first #architecture
```
Tags become searchable and filterable in Obsidian's tag pane.
### Frontmatter
```yaml
---
title: Document Title
type: note
tags: [search, design]
---
```
Frontmatter provides metadata for Obsidian to use in search and filtering.
## Canvas Integration
Basic Memory can create [Obsidian Canvas](https://obsidian.md/canvas) files:
1. Ask Claude to create a visualization:
```
You: "Create a canvas showing the structure of our project components."
```
2. Claude generates a .canvas file in your knowledge base
3. Open the file in Obsidian to view and edit the visual representation
4. Canvas files maintain references to your documents
## Recommended Plugins
These Obsidian plugins work especially well with Basic Memory:
- **Dataview**: Query your knowledge base programmatically
- **Kanban**: Organize tasks from knowledge files
- **Calendar**: View and navigate temporal knowledge
- **Templates**: Create consistent knowledge structures
## Workflow Suggestions
### Daily Notes
```markdown
# 2024-01-21
## Progress
- Updated [[Search Design]]
- Fixed [[Bug Report 123]]
## Notes
- [idea] Better indexing #enhancement
- [todo] Update docs #documentation
## Links
- relates_to [[Current Sprint]]
- updates [[Project Status]]
```
### Project Tracking
```markdown
# Current Sprint
## Tasks
- [ ] Update [[Search]]
- [ ] Fix [[Auth Bug]]
## Tags
#sprint #planning #current
```
## Relations
- enhances [[Introduction to Basic Memory]] (Overview of system)
- relates_to [[Canvas]] (Visual knowledge mapping)
- complements [[User Guide]] (Using Basic Memory)
-311
View File
@@ -1,311 +0,0 @@
# Supabase OAuth Setup for Basic Memory
This guide explains how to set up Supabase as the OAuth provider for Basic Memory MCP server in production.
## Prerequisites
1. A Supabase project (create one at [supabase.com](https://supabase.com))
2. Basic Memory MCP server deployed
3. Environment variables configuration
## Overview
The Supabase OAuth provider offers:
- Production-ready authentication with persistent storage
- User management through Supabase Auth
- JWT token validation
- Integration with Supabase's security features
- Support for social logins (GitHub, Google, etc.)
## Setup Steps
### 1. Get Supabase Credentials
From your Supabase project dashboard:
1. Go to Settings > API
2. Copy these values:
- `Project URL``SUPABASE_URL`
- `anon public` key → `SUPABASE_ANON_KEY`
- `service_role` key → `SUPABASE_SERVICE_KEY` (keep this secret!)
- JWT secret → `SUPABASE_JWT_SECRET` (under Settings > API > JWT Settings)
### 2. Configure Environment Variables
Create a `.env` file:
```bash
# Enable OAuth
FASTMCP_AUTH_ENABLED=true
FASTMCP_AUTH_PROVIDER=supabase
# Your MCP server URL
FASTMCP_AUTH_ISSUER_URL=https://your-mcp-server.com
# Supabase configuration
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-key
SUPABASE_JWT_SECRET=your-jwt-secret
# Allowed OAuth clients (comma-separated)
SUPABASE_ALLOWED_CLIENTS=web-app,mobile-app,cli-tool
# Required scopes
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
```
### 3. Create OAuth Clients Table (Optional)
For production, create a table to store OAuth clients in Supabase:
```sql
CREATE TABLE oauth_clients (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
client_id TEXT UNIQUE NOT NULL,
client_secret TEXT NOT NULL,
name TEXT,
redirect_uris TEXT[],
allowed_scopes TEXT[],
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create an index for faster lookups
CREATE INDEX idx_oauth_clients_client_id ON oauth_clients(client_id);
-- RLS policies
ALTER TABLE oauth_clients ENABLE ROW LEVEL SECURITY;
-- Only service role can manage clients
CREATE POLICY "Service role can manage clients" ON oauth_clients
FOR ALL USING (auth.jwt()->>'role' = 'service_role');
```
### 4. Set Up Auth Flow
The Supabase OAuth provider handles the following flow:
1. **Client Authorization Request**
```
GET /authorize?client_id=web-app&redirect_uri=https://app.com/callback
```
2. **Redirect to Supabase Auth**
- User authenticates with Supabase (email/password, magic link, or social login)
- Supabase redirects back to your MCP server
3. **Token Exchange**
```
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=xxx&client_id=web-app
```
4. **Access Protected Resources**
```
GET /mcp
Authorization: Bearer <access_token>
```
### 5. Enable Social Logins (Optional)
In Supabase dashboard:
1. Go to Authentication > Providers
2. Enable desired providers (GitHub, Google, etc.)
3. Configure OAuth apps for each provider
4. Users can now log in via social providers
### 6. User Management
Supabase provides:
- User registration and login
- Password reset flows
- Email verification
- User metadata storage
- Admin APIs for user management
Access user data in your MCP tools:
```python
# In your MCP tool
async def get_user_info(ctx: Context):
# The token is already validated by the OAuth middleware
user_id = ctx.auth.user_id
email = ctx.auth.email
# Use Supabase client to get more user data if needed
user = await supabase.auth.admin.get_user_by_id(user_id)
return user
```
### 7. Production Deployment
1. **Environment Security**
- Never expose `SUPABASE_SERVICE_KEY`
- Use environment variables, not hardcoded values
- Rotate keys periodically
2. **HTTPS Required**
- Always use HTTPS in production
- Configure proper SSL certificates
3. **Rate Limiting**
- Implement rate limiting for auth endpoints
- Use Supabase's built-in rate limiting
4. **Monitoring**
- Monitor auth logs in Supabase dashboard
- Set up alerts for suspicious activity
## Testing
### Local Development
For local testing with Supabase:
```bash
# Start MCP server with Supabase auth
FASTMCP_AUTH_ENABLED=true \
FASTMCP_AUTH_PROVIDER=supabase \
SUPABASE_URL=http://localhost:54321 \
SUPABASE_ANON_KEY=your-local-anon-key \
bm mcp --transport streamable-http
```
### Test Authentication Flow
```python
import httpx
import asyncio
async def test_supabase_auth():
# 1. Register/login with Supabase directly
supabase_url = "https://your-project.supabase.co"
# 2. Get MCP authorization URL
response = await httpx.get(
"http://localhost:8000/authorize",
params={
"client_id": "web-app",
"redirect_uri": "http://localhost:3000/callback",
"response_type": "code",
}
)
# 3. User logs in via Supabase
# 4. Exchange code for MCP tokens
# 5. Access protected resources
asyncio.run(test_supabase_auth())
```
## Advanced Configuration
### Custom User Metadata
Store additional user data in Supabase:
```sql
-- Add custom fields to auth.users
ALTER TABLE auth.users
ADD COLUMN IF NOT EXISTS metadata JSONB DEFAULT '{}';
-- Or create a separate profiles table
CREATE TABLE profiles (
id UUID REFERENCES auth.users PRIMARY KEY,
username TEXT UNIQUE,
avatar_url TEXT,
bio TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
```
### Row Level Security (RLS)
Protect user data with RLS:
```sql
-- Users can only access their own data
CREATE POLICY "Users can view own profile" ON profiles
FOR SELECT USING (auth.uid() = id);
CREATE POLICY "Users can update own profile" ON profiles
FOR UPDATE USING (auth.uid() = id);
```
### Custom Claims
Add custom claims to JWT tokens:
```sql
-- Function to add custom claims
CREATE OR REPLACE FUNCTION custom_jwt_claims()
RETURNS JSON AS $$
BEGIN
RETURN json_build_object(
'user_role', current_setting('request.jwt.claims')::json->>'user_role',
'permissions', current_setting('request.jwt.claims')::json->>'permissions'
);
END;
$$ LANGUAGE plpgsql;
```
## Troubleshooting
### Common Issues
1. **Invalid JWT Secret**
- Ensure `SUPABASE_JWT_SECRET` matches your Supabase project
- Check Settings > API > JWT Settings in Supabase dashboard
2. **CORS Errors**
- Configure CORS in your MCP server
- Add allowed origins in Supabase dashboard
3. **Token Validation Fails**
- Verify tokens are being passed correctly
- Check token expiration times
- Ensure scopes match requirements
4. **User Not Found**
- Confirm user exists in Supabase Auth
- Check if email is verified (if required)
- Verify client permissions
### Debug Mode
Enable debug logging:
```bash
export FASTMCP_LOG_LEVEL=DEBUG
export SUPABASE_LOG_LEVEL=debug
```
## Security Best Practices
1. **Secure Keys**: Never commit secrets to version control
2. **Least Privilege**: Use minimal required scopes
3. **Token Rotation**: Implement refresh token rotation
4. **Audit Logs**: Monitor authentication events
5. **Rate Limiting**: Protect against brute force attacks
6. **HTTPS Only**: Always use encrypted connections
## Migration from Basic Auth
To migrate from the basic auth provider:
1. Export existing user data
2. Import users into Supabase Auth
3. Update client applications to use new auth flow
4. Gradually transition users to Supabase login
## Next Steps
- Set up email templates in Supabase
- Configure password policies
- Implement MFA (multi-factor authentication)
- Add social login providers
- Create admin dashboard for user management
-243
View File
@@ -1,243 +0,0 @@
---
title: Technical Information
type: note
permalink: docs/technical-information
---
# Technical Information
This document provides technical details about Basic Memory's implementation, licensing, and integration with the Model Context Protocol (MCP).
## Architecture
Basic Memory consists of:
1. **Core Knowledge Engine**: Parses and indexes Markdown files
2. **SQLite Database**: Provides fast querying and search
3. **MCP Server**: Implements the Model Context Protocol
4. **CLI Tools**: Command-line utilities for management
5. **Sync Service**: Monitors file changes and updates the database
The system follows a file-first architecture where all knowledge is represented in standard Markdown files and the database serves as a secondary index.
## Model Context Protocol (MCP)
Basic Memory implements the [Model Context Protocol](https://github.com/modelcontextprotocol/spec), an open standard for enabling AI models to access external tools:
- **Standardized Interface**: Common protocol for tool integration
- **Tool Registration**: Basic Memory registers as a tool provider
- **Asynchronous Communication**: Enables efficient interaction with AI models
- **Standardized Schema**: Structured data exchange format
Integration with Claude Desktop uses the MCP to grant Claude access to your knowledge base through a set of specialized tools that search, read, and write knowledge.
## Licensing
Basic Memory is licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html):
- **Free Software**: You can use, study, share, and modify the software
- **Copyleft**: Derivative works must be distributed under the same license
- **Network Use**: Network users must be able to receive the source code
- **Commercial Use**: Allowed, subject to license requirements
The AGPL license ensures Basic Memory remains open source while protecting against proprietary forks.
## Source Code
Basic Memory is developed as an open-source project:
- **GitHub Repository**: [https://github.com/basicmachines-co/basic-memory](https://github.com/basicmachines-co/basic-memory)
- **Issue Tracker**: Report bugs and request features on GitHub
- **Contributions**: Pull requests are welcome following the contributing guidelines
- **Documentation**: Source for this documentation is also available in the repository
## Data Storage and Privacy
Basic Memory is designed with privacy as a core principle:
- **Local-First**: All data remains on your local machine
- **No Cloud Dependency**: No remote servers or accounts required
- **Telemetry**: Optional and disabled by default
- **Standard Formats**: All data is stored in standard file formats you control
## Implementation Details
Knowledge in Basic Memory is organized as a semantic graph:
1. **Entities** - Distinct concepts represented by Markdown documents
2. **Observations** - Categorized facts and information about entities
3. **Relations** - Connections between entities that form the knowledge graph
This structure emerges from simple text patterns in standard Markdown:
```markdown
---
title: Coffee Brewing Methods
type: note
permalink: coffee/coffee-brewing-methods
tags:
- '#coffee'
- '#brewing'
- '#methods'
- '#demo'
---
# Coffee Brewing Methods
An exploration of different coffee brewing techniques, their characteristics, and how they affect flavor extraction.
## Overview
Coffee brewing is both an art and a science. Different brewing methods extract different compounds from coffee beans,
resulting in unique flavor profiles, body, and mouthfeel. The key variables in any brewing method are:
- Grind size
- Water temperature
- Brew time
- Coffee-to-water ratio
- Agitation/turbulence
## Observations
- [principle] Coffee extraction follows a predictable pattern: acids extract first, then sugars, then bitter compounds
#extraction
- [method] Pour over methods generally produce cleaner, brighter cups with more distinct flavor notes #clarity
## Relations
- requires [[Proper Grinding Technique]]
- affects [[Flavor Extraction]]
```
Becomes
```json
{
"entities": [
{
"permalink": "coffee/coffee-brewing-methods",
"title": "Coffee Brewing Methods",
"file_path": "Coffee Notes/Coffee Brewing Methods.md",
"entity_type": "note",
"entity_metadata": {
"title": "Coffee Brewing Methods",
"type": "note",
"permalink": "coffee/coffee-brewing-methods",
"tags": "['#coffee', '#brewing', '#methods', '#demo']"
},
"checksum": "bfa32a0f23fa124b53f0694c344d2788b0ce50bd090b55b6d738401d2a349e4c",
"content_type": "text/markdown",
"observations": [
{
"category": "principle",
"content": "Coffee extraction follows a predictable pattern: acids extract first, then sugars, then bitter compounds #extraction",
"tags": [
"extraction"
],
"permalink": "coffee/coffee-brewing-methods/observations/principle/coffee-extraction-follows-a-predictable-pattern-acids-extract-first-then-sugars-then-bitter-compounds-extraction"
},
{
"category": "method",
"content": "Pour over methods generally produce cleaner, brighter cups with more distinct flavor notes #clarity",
"tags": [
"clarity"
],
"permalink": "coffee/coffee-brewing-methods/observations/method/pour-over-methods-generally-produce-cleaner-brighter-cups-with-more-distinct-flavor-notes-clarity"
}
],
"relations": [
{
"from_id": "coffee/coffee-bean-origins",
"to_id": "coffee/coffee-brewing-methods",
"relation_type": "pairs_with",
"permalink": "coffee/coffee-bean-origins/pairs-with/coffee/coffee-brewing-methods",
"to_name": "Coffee Brewing Methods"
},
{
"from_id": "coffee/flavor-extraction",
"to_id": "coffee/coffee-brewing-methods",
"relation_type": "affected_by",
"permalink": "coffee/flavor-extraction/affected-by/coffee/coffee-brewing-methods",
"to_name": "Coffee Brewing Methods"
}
],
"created_at": "2025-03-06T14:01:23.445071",
"updated_at": "2025-03-06T13:34:48.563606"
}
]
}
```
Basic Memory understands how to build context via its semantic graph.
### Entity Model
Basic Memory's core data model consists of:
- **Entities**: Documents in your knowledge base
- **Observations**: Facts or statements about entities
- **Relations**: Connections between entities
- **Tags**: Additional categorization for entities and observations
The system parses Markdown files to extract this structured information while preserving the human-readable format.
### Files as Source of Truth
Plain Markdown files store all knowledge, making it accessible with any text editor and easy to version with git.
```mermaid
flowchart TD
User((User)) <--> |Conversation| Claude["Claude or other LLM"]
Claude <-->|API Calls| BMCP["Basic Memory MCP Server"]
subgraph "Local Storage"
KnowledgeFiles["Markdown Files - Source of Truth"]
KnowledgeIndex[(Knowledge Graph SQLite Index)]
end
BMCP <-->|"write_note() read_note()"| KnowledgeFiles
BMCP <-->|"search_notes() build_context()"| KnowledgeIndex
KnowledgeFiles <-.->|Sync Process| KnowledgeIndex
KnowledgeFiles <-->|Direct Editing| Editors((Text Editors & Git))
User -.->|"Complete control, Privacy preserved"| KnowledgeFiles
class Claude primary
class BMCP secondary
class KnowledgeFiles tertiary
class KnowledgeIndex quaternary
class User,Editors user`;
```
### Sqlite Database
A local SQLite database maintains the knowledge graph topology for fast queries and semantic traversal without cloud dependencies. It contains:
- db tables for the knowledge graph schema
- a search index table enabling full text search across the knowledge base
### Sync Process
The sync process:
1. Detects changes to files in the knowledge directory
2. Parses modified files to extract structured data
3. Updates the SQLite database with changes
4. Resolves forward references when new entities are created
5. Updates the search index for fast querying
### Search Engine
The search functionality:
1. Uses a combination of full-text search and semantic matching
2. Indexes observations, relations, and content
3. Supports wildcards and pattern matching in memory:// URLs
4. Traverses the knowledge graph to follow relationships
5. Ranks results by relevance to the query
## Relations
- relates_to [[Welcome to Basic memory]] (Overview)
- relates_to [[CLI Reference]] (Command line tools)
- implements [[Knowledge Format]] (File structure and format)
-657
View File
@@ -1,657 +0,0 @@
---
title: User Guide
type: note
permalink: docs/user-guide
---
# User Guide
This guide explains how to effectively use Basic Memory in your daily workflow, from creating knowledge through
conversations to building a rich semantic network.
## Basic Memory Workflow
Using Basic Memory follows a natural cycle:
1. **Have conversations** with AI assistants like Claude
2. **Capture knowledge** in Markdown files
3. **Build connections** between pieces of knowledge
4. **Reference your knowledge** in future conversations
5. **Edit files directly** when needed
6. **Sync changes** automatically
## Creating Knowledge
### Through Conversations
To create knowledge during conversations with Claude:
```
You: We've covered several authentication approaches. Could you create a note summarizing what we've discussed?
Claude: I'll create a note summarizing our authentication discussion.
```
This creates a Markdown file in your `~/basic-memory` directory with semantic markup.
### Direct File Creation
You can create files directly:
1. Create a new Markdown file in your `~/basic-memory` directory
2. Add frontmatter with title, type, and optional tags
3. Structure content with observations and relations
4. Save the file
5. Run `basic-memory sync` if not in watch mode
## Using Special Prompts
Basic Memory includes several special prompts that help you leverage your knowledge base more effectively. In apps like
Claude Desktop, these prompts trigger specific tools to search and analyze your knowledge base.
### Continue Conversation
When you want to pick up where you left off on a topic:
```
You: Let's continue our conversation about authentication systems.
```
Behind the scenes:
- Claude searches your knowledge base for content about "authentication systems"
- It retrieves relevant documents and their relations
- It analyzes the context to understand where you left off
- It builds a comprehensive picture of what you've previously discussed
- It can then resume the conversation with all that context
This is particularly useful when:
- Starting a new session days or weeks after your last discussion
- Switching between multiple ongoing projects
- Building on previous work without repeating yourself
### Recent Activity
To get an overview of what you've been working on:
```
You: What have we been discussing recently?
```
Behind the scenes:
- Claude retrieves documents modified recently
- It analyzes patterns and themes
- It summarizes the key topics and changes
- It offers to continue working on any of those topics
This is useful for:
- Coming back after a break
- Getting a quick reminder of ongoing projects
- Deciding what to work on next
### Search
To find specific information in your knowledge base:
```
You: Find information about JWT authentication in my notes.
```
Behind the scenes:
- Claude performs a semantic search for "JWT authentication"
- It retrieves and ranks the most relevant documents
- It summarizes the key findings
- It offers to explore specific areas in more detail
This is useful for:
- Finding specific information quickly
- Exploring what you know about a topic
- Starting work on an existing topic
### Example
Choose "Continue Conversation"
![[prompt 1.png|500]]
Enter a topic
![[prompt2.png|500]]
Give instructions
![[prompt3.png|500]]
Claude Desktop lets you send a prompt to provide context. You can use this at the beginning of a chat to preload context
without needing to copy paste all the time. By using one of the supplied prompts, Basic Memory will search the knowledge
base and give the AI instructions for how to build context.
Choose "Continue Conversation":
![[prompt 1.png|500]]
Enter a topic:
![[prompt2.png|500]]
Give optional additional instructions:
![[prompt3.png|500]]
Claude can build context from the supplied topic. This works independently of Claude Project information. All the
context comes from your local knowledge base.
![[prompt4.png|500]]
## Searching Your Knowledge Base
Basic Memory provides multiple ways to search and explore your knowledge base:
### Natural Language Search
The simplest way to search is to ask Claude directly:
```
You: What do I know about authentication methods?
```
Claude will search your knowledge base semantically and return relevant information.
### Search Prompt
Use the dedicated search prompt for more focused searches:
```
You: Search for "JWT authentication"
```
This triggers a specialized search that returns precise results with document titles, relevant excerpts, and offers to
explore specific documents.
### Boolean Search
For more precise searches, use boolean operators to refine your queries:
```
You: Search for "authentication AND OAuth NOT basic"
```
Basic Memory supports standard boolean operators:
- **AND**: Find documents containing both terms
```
You: Search for "python AND flask"
```
This finds documents containing both "python" and "flask"
- **OR**: Find documents containing either term
```
You: Search for "python OR javascript"
```
This finds documents containing either "python" or "javascript"
- **NOT**: Exclude documents containing specific terms
```
You: Search for "python NOT django"
```
This finds documents containing "python" but excludes those containing "django"
- **Grouping with parentheses**: Control operator precedence
```
You: Search for "(python OR javascript) AND web"
```
This finds documents about web development that mention either Python or JavaScript
Boolean search is particularly useful for:
- Narrowing down results in large knowledge bases
- Finding specific combinations of concepts
- Excluding irrelevant content from search results
- Creating complex queries for precise information retrieval
### Memory URL Pattern Matching
For advanced searches, use memory:// URL patterns with wildcards:
```
You: Look at memory://auth* and summarize all authentication approaches.
```
Pattern matching supports:
- **Wildcards**: `memory://auth*` matches all permalinks starting with "auth"
- **Path patterns**: `memory://project/*/auth` matches auth documents in any project subfolder
- **Relation traversal**: `memory://auth-system/implements/*` finds all documents that implement the auth system
### Combining Search with Context Building
The most powerful searches build comprehensive context by following relationships:
```
You: Search for JWT authentication and then follow all implementation relations.
```
This builds a complete picture by:
1. Finding documents about JWT authentication
2. Following implementation relationships from those documents
3. Building a complete picture of how JWT is implemented across your system
### Search Best Practices
For effective searching:
1. **Be specific** with search terms and phrases
2. **Use boolean operators** to refine searches and find precise information
3. **Use technical terms** when searching for technical content
4. **Follow up** on search results by asking for more details about specific documents
5. **Combine approaches** by starting with search and then using memory:// URLs for precision
6. **Use relation traversal** to explore connected concepts after finding initial documents
## Referencing Knowledge
### Using memory:// URLs
Reference specific knowledge directly:
```
You: Please look at memory://authentication-approaches and suggest which approach would be best for our mobile app.
```
### Natural Language References
Reference knowledge conversationally:
```
You: What did we decide about authentication for the project?
```
### Advanced References
Follow connections across your knowledge graph:
```
You: Look at memory://project-architecture and check related documents to give me a complete picture.
```
## Working with Files
### File Location and Organization
By default, Basic Memory stores files in `~/basic-memory`:
- Browse this directory in your file explorer
- Organize files into subfolders
- Use git for version control
### File Format
Each knowledge file follows this structure:
```markdown
---
title: Authentication Approaches
type: note
tags: [security, architecture]
permalink: authentication-approaches
---
# Authentication Approaches
A comparison of authentication methods.
## Observations
- [approach] JWT provides stateless authentication #security
- [limitation] Session tokens require server-side storage #infrastructure
## Relations
- implements [[Security Requirements]]
- affects [[User Login Flow]]
```
### Editing Files
Modify files in any text editor:
1. Open the file in your preferred editor
2. Make changes to content, observations, or relations
3. Save the file
4. Basic Memory detects changes automatically when running in watch mode
## Building a Knowledge Graph
The value of Basic Memory comes from connections between pieces of knowledge.
### Creating Relations
When creating or editing notes, build connections:
```markdown
## Relations
- implements [[Security Requirements]]
- depends_on [[User Authentication]]
```
Relations can be:
- Hierarchical (part_of, contains)
- Directional (implements, depends_on)
- Associative (relates_to, similar_to)
- Temporal (precedes, follows)
Relations are also created via regular wiki-link style links within the body text.
### Forward References
Reference documents that don't exist yet:
```markdown
- will_impact [[Future Feature]]
```
These references resolve automatically when you create the referenced document.
## Conversation Continuity
Basic Memory maintains context across different conversations.
### Starting New Sessions with Context
When starting a new conversation with Claude, you can:
1. **Use special prompts** like "Continue conversation about..." or "What were we working on?"
2. **Reference specific documents** with memory:// URLs
3. **Ask about recent work** with "What have we been discussing recently?"
4. **Search for specific topics** with "Find information about..."
### Long-Term Projects
Maintain context for complex projects over time:
1. **Document key decisions** as you make them
2. **Create relationships** between project components
3. **Reference past decisions** when implementing features
4. **Update documentation** as the project evolves
### Tips for Effective Continuity
1. **Be specific about topics** when continuing a conversation
2. **Reference documents directly** with memory:// URLs for precision
3. **Create summary notes** after important discussions
4. **Update existing notes** rather than creating duplicates
5. **Build robust connections** between related topics
## Advanced Features
### Note Editing (New in v0.13.0)
**Edit notes incrementally without rewriting entire documents:**
```
💬 "Add a new section about deployment to my API documentation"
🤖 [Uses edit_note to append new section]
💬 "Update the date at the top of my meeting notes"
🤖 [Uses edit_note to prepend new timestamp]
💬 "Replace the implementation section in my design doc"
🤖 [Uses edit_note to replace specific section]
```
Available editing operations:
- **Append**: Add content to end of notes
- **Prepend**: Add content to beginning of notes
- **Replace Section**: Replace content under specific headers
- **Find & Replace**: Simple text replacements with validation
### File Management (New in v0.13.0)
**Move and organize notes with full database consistency:**
```
💬 "Move my old meeting notes to the archive folder"
🤖 [Uses move_note with automatic folder creation and database updates]
💬 "Reorganize my project files into a better structure"
🤖 [Moves files while maintaining search indexes and links]
```
Move operations include:
- **Database Consistency**: Updates file paths, permalinks, and checksums
- **Search Reindexing**: Maintains search functionality after moves
- **Folder Creation**: Automatically creates destination directories
- **Project Isolation**: Moves are contained within the current project
- **Rollback Protection**: Ensures data integrity during failed operations
### Enhanced Search (New in v0.13.0)
**Frontmatter tags are now searchable:**
```yaml
---
title: Coffee Brewing Methods
tags: [coffee, brewing, equipment]
---
```
Now searchable by: "coffee", "brewing", "equipment", or "Coffee Brewing Methods"
### Importing External Knowledge
Import existing conversations:
```bash
# From Claude
basic-memory import claude conversations
# From ChatGPT
basic-memory import chatgpt
# Target specific projects (v0.13.0)
basic-memory --project=work import claude conversations
```
After importing, changes sync automatically in real-time.
### Obsidian Integration
Use with [Obsidian](https://obsidian.md):
1. Point Obsidian to your `~/basic-memory` directory
2. Use Obsidian's graph view to visualize your knowledge network
3. All changes sync back to Basic Memory
### Canvas Visualizations
Create visual knowledge maps:
```
You: Could you create a canvas visualization of our project components?
```
This generates an Obsidian canvas file showing the relationships between concepts.
### Advanced Memory URI Patterns
Use wildcards and patterns:
```
You: Review memory://project/*/requirements to summarize all project requirements.
```
## Command Line Interface
### Sync Commands
```bash
# One-time sync
basic-memory sync
# Watch for changes
basic-memory sync --watch
```
### Status and Information
```bash
# Check system status
basic-memory status
# View CLI help
basic-memory --help
```
### Import Commands
```bash
# Import from Claude
basic-memory import claude conversations
# Import from ChatGPT
basic-memory import chatgpt
```
## Multiple Projects (v0.13.0)
Basic Memory v0.13.0 introduces **fluid project management** - the ability to switch between projects instantly during conversations without restart. This allows you to maintain separate knowledge graphs for different purposes while seamlessly switching between them.
### Instant Project Switching (New in v0.13.0)
**Switch projects during conversations:**
```
💬 "What projects do I have?"
🤖 Available projects:
• main (current, default)
• work-notes
• personal-journal
• code-snippets
💬 "Switch to work-notes"
🤖 ✓ Switched to work-notes project
Project Summary:
• 47 entities
• 125 observations
• 23 relations
💬 "What did I work on yesterday?"
🤖 [Shows recent activity from work-notes project]
```
### Project-Specific Operations (New in v0.13.0)
Some MCP tools support optional project parameters for targeting specific projects:
```
💬 "Create a note about this meeting in my personal-notes project"
🤖 [Creates note in personal-notes project]
💬 "Switch to my work project"
🤖 [Switches project context, then all operations work within that project]
```
**Note**: Operations like search, move, and edit work within the currently active project. To work with content in different projects, switch to that project first or use the project parameter where supported.
### Managing Projects
```bash
# List all configured projects
basic-memory project list
# Add a new project
basic-memory project create work ~/work-basic-memory
# Set the default project
basic-memory project set-default work
# Remove a project (doesn't delete files)
basic-memory project delete personal
# Show current project statistics
basic-memory project info
```
### 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
```
### Unified Database Architecture (New in v0.13.0)
Basic Memory v0.13.0 uses a unified database architecture:
- **Single Database**: All projects share `~/.basic-memory/memory.db`
- **Project Isolation**: Proper data separation with project context
- **Better Performance**: Optimized queries and reduced file I/O
- **Easier Backup**: Single database file contains all project data
- **Session Context**: Maintains active project throughout conversations
## Workflow Tips
### General Workflow
1. **Project Organization**: Use multiple projects to separate different areas (work, personal, research)
2. **Session Context**: Switch projects during conversations without restart (v0.13.0)
3. **Real-time Sync**: Changes sync automatically - no need to run watch mode
4. **Review Content**: Edit AI-created content for accuracy
5. **Build Connections**: Create rich relationships between related ideas
6. **Use Special Prompts**: Start conversations with context from your knowledge base
### v0.13.0 Workflow Enhancements
7. **Incremental Editing**: Use edit_note for small changes instead of rewriting entire documents
8. **File Organization**: Move and reorganize notes as your knowledge base grows
9. **Project-Specific Creation**: Create notes in specific projects using project parameters
10. **Search Tags**: Use frontmatter tags to improve content discoverability
11. **Project Statistics**: Monitor project growth and activity with project info commands
## Troubleshooting
### Sync Issues
If changes aren't showing up:
1. Run `basic-memory status` to check system state
2. Try a manual sync with `basic-memory sync`
### Missing Content
If content isn't found:
1. Check the exact path and permalink
2. Try searching with more general terms
3. Verify the file exists in your knowledge base
### Relation Problems
If relations aren't working:
1. Ensure exact title matching in [[WikiLinks]]
2. Check for typos in relation types
3. Verify both documents exist
## Relations
- implements [[Knowledge Format]] (How knowledge is structured)
- relates_to [[Getting Started with Basic Memory]] (Setup and first steps)
- relates_to [[Canvas]] (Creating visual knowledge maps)
- relates_to [[CLI Reference]] (Command line tools)
- enhanced_in_v0.13.0 [[OAuth Authentication Guide]] (Production authentication)
- enhanced_in_v0.13.0 [[Project Management]] (Multi-project workflows)
-68
View File
@@ -1,68 +0,0 @@
---
title: Introduction to Basic Memory
type: docs
permalink: docs/introduction
tags:
- documentation
- index
- overview
---
# BASIC MEMORY
Basic Memory is a knowledge management system that allows you to build a persistent semantic graph from conversations
with AI assistants. All knowledge is stored in standard Markdown files on your computer, giving you full control and
ownership of your data.
Basic Memory connects you and AI assistants through shared knowledge:
1. **Captures knowledge** from natural conversations with AI assistants
2. **Structures information** using simple semantic patterns in Markdown
3. **Enables knowledge reuse** across different conversations and sessions
4. **Maintains persistence** through local files you control completely
Both you and AI assistants like Claude can read from and write to the same knowledge base, creating a continuous
learning environment where each conversation builds upon previous ones.
## Pick up your conversation right where you left off
- 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)
- **Model Context Protocol (MCP)** - For seamless AI assistant integration
Basic Memory gives you complete control over your knowledge:
- **Local-first storage** - All knowledge lives on your computer
- **Standard file formats** - Plain Markdown compatible with any editor
- **Directory organization** - Knowledge stored in `~/basic-memory` by default
- **Version control ready** - Use git for history, branching, and collaboration
- **Edit anywhere** - Modify files with any text editor or Obsidian
Changes to files automatically sync with the knowledge graph, and AI assistants can see your edits in conversations.
## Documentation Map
Continue exploring Basic Memory with these guides:
- Installation and setup [[Getting Started with Basic Memory]]
- Comprehensive usage instructions [[User Guide]]
- Detailed explanation of knowledge structure [[Knowledge Format]]
- Obsidian integration guide [[Obsidian Integration]]
- Canvas visualization guide [[Canvas]]
- Command line tool reference [[CLI Reference]]
- Reference for AI assistants using Basic Memory [[AI Assistant Guide]]
- Technical implementation details [[Technical Information]]
## Next Steps
Start with the [[Getting Started with Basic Memory]] guide to install Basic Memory and configure it with your AI
assistant.
Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 908 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

-5
View File
@@ -1,5 +0,0 @@
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);
-3
View File
@@ -1,3 +0,0 @@
{
"promptDelete": false
}
-1
View File
@@ -1 +0,0 @@
{}
-3
View File
@@ -1,3 +0,0 @@
[
"optimize-canvas-connections"
]
-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
}
@@ -1,158 +0,0 @@
/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// main.ts
var main_exports = {};
__export(main_exports, {
default: () => OptimizeCanvasConnectionsPlugin
});
module.exports = __toCommonJS(main_exports);
var import_obsidian = require("obsidian");
var OptimizeCanvasConnectionsPlugin = class extends import_obsidian.Plugin {
async onload() {
this.addCommand({
id: "optimize-preserve-axes-selection",
name: "Optimize selection (preserve axes)",
checkCallback: (checking) => {
const canvasView = app.workspace.getActiveViewOfType(import_obsidian.ItemView);
if ((canvasView == null ? void 0 : canvasView.getViewType()) == "canvas") {
if (!checking) {
this.optimize("preserve-axes");
}
return true;
}
return false;
}
});
this.addCommand({
id: "optimize-shortest-path-selection",
name: "Optimize selection (shortest path)",
checkCallback: (checking) => {
const canvasView = app.workspace.getActiveViewOfType(import_obsidian.ItemView);
if ((canvasView == null ? void 0 : canvasView.getViewType()) == "canvas") {
if (!checking) {
this.optimize("shortest-path");
}
return true;
}
return false;
}
});
}
onunload() {
}
async optimize(option) {
const canvasView = app.workspace.getActiveViewOfType(import_obsidian.ItemView);
const canvas = canvasView == null ? void 0 : canvasView.canvas;
const currentSelection = canvas == null ? void 0 : canvas.selection;
let selectedIDs = new Array();
currentSelection.forEach(function(selection) {
selectedIDs.push(selection.id);
});
let applyToAll = false;
if (selectedIDs.length == 0) {
applyToAll = true;
}
for (let [edgeKey, edge] of canvas["edges"]) {
let fromNode = edge["from"]["node"];
let toNode = edge["to"]["node"];
let fromPossibilities = [edge["from"]["side"]];
if (applyToAll || selectedIDs.includes(fromNode["id"])) {
switch (option) {
case "shortest-path":
fromPossibilities = ["top", "bottom", "left", "right"];
break;
case "preserve-axes":
switch (edge["from"]["side"]) {
case "top":
case "bottom":
fromPossibilities = ["top", "bottom"];
break;
case "left":
case "right":
fromPossibilities = ["left", "right"];
break;
}
}
}
let toPossibilities = [edge["to"]["side"]];
if (applyToAll || selectedIDs.includes(toNode["id"])) {
switch (option) {
case "shortest-path":
toPossibilities = ["top", "bottom", "left", "right"];
break;
case "preserve-axes":
switch (edge["to"]["side"]) {
case "top":
case "bottom":
toPossibilities = ["top", "bottom"];
break;
case "left":
case "right":
toPossibilities = ["left", "right"];
break;
}
}
}
let distances = [];
for (const fromSide of fromPossibilities) {
let fromPoint = { "x": 0, "y": 0 };
if (fromSide == "top") {
fromPoint = { "x": fromNode["x"] + fromNode["width"] / 2, "y": fromNode["y"] };
} else if (fromSide == "bottom") {
fromPoint = { "x": fromNode["x"] + fromNode["width"] / 2, "y": fromNode["y"] + fromNode["height"] };
} else if (fromSide == "left") {
fromPoint = { "x": fromNode["x"], "y": fromNode["y"] + fromNode["height"] / 2 };
} else if (fromSide == "right") {
fromPoint = { "x": fromNode["x"] + fromNode["width"], "y": fromNode["y"] + fromNode["height"] / 2 };
}
for (const toSide of toPossibilities) {
let toPoint = { "x": 0, "y": 0 };
if (toSide == "top") {
toPoint = { "x": toNode["x"] + toNode["width"] / 2, "y": toNode["y"] };
} else if (toSide == "bottom") {
toPoint = { "x": toNode["x"] + toNode["width"] / 2, "y": toNode["y"] + toNode["height"] };
} else if (toSide == "left") {
toPoint = { "x": toNode["x"], "y": toNode["y"] + toNode["height"] / 2 };
} else if (toSide == "right") {
toPoint = { "x": toNode["x"] + toNode["width"], "y": toNode["y"] + toNode["height"] / 2 };
}
distances.push({
"fromSide": fromSide,
"toSide": toSide,
"distance": (toPoint.x - fromPoint.x) ** 2 + (toPoint.y - fromPoint.y) ** 2
});
}
}
distances = distances.sort(function(a, b) {
return a.distance - b.distance;
});
edge["from"]["side"] = distances[0]["fromSide"];
edge["to"]["side"] = distances[0]["toSide"];
edge.render();
}
canvas.requestSave();
}
};
/* nosourcemap */
@@ -1,10 +0,0 @@
{
"id": "optimize-canvas-connections",
"name": "Optimize Canvas Connections",
"version": "1.0.0",
"minAppVersion": "1.1.9",
"description": "An Obsidian plugin that declutters a canvas by reconnecting notes using their nearest edges.",
"author": "Félix Chénier",
"authorUrl": "https://felixchenier.uqam.ca",
"isDesktopOnly": false
}
-6
View File
@@ -1,6 +0,0 @@
{
"siteId": null,
"host": null,
"included": [],
"excluded": []
}
@@ -1,83 +0,0 @@
---
title: Brewing Equipment
type: note
permalink: coffee/brewing-equipment
tags:
- '#coffee'
- '#equipment'
- '#gear'
- '#brewing'
- '#demo'
---
# Brewing Equipment
Essential tools and equipment for brewing coffee, their characteristics, and how they affect the brewing process.
## Overview
The equipment used to brew coffee plays a crucial role in determining the final cup quality. From grinders to brewers to kettles, each piece of equipment contributes to different aspects of the brewing process.
## Observations
- [principle] Equipment quality often has a bigger impact on consistency than on absolute quality potential #quality
- [principle] Good grind consistency is the most important technical factor in extraction quality #grind
- [investment] A good burr grinder is often the most important investment for improving home coffee #gear
- [technique] Equipment maintenance and cleaning significantly impact flavor consistency over time #maintenance
## Grinders
- [equipment] Burr grinders crush beans between two abrasive surfaces for more consistent particle size #grinders
- [equipment] Blade grinders chop beans unevenly, leading to inconsistent extraction #grinders
- [equipment] Flat burr grinders produce very consistent particle size but generate more heat #burrs
- [equipment] Conical burr grinders create slightly less uniform grounds but with less heat and noise #burrs
- [feature] Grind adjustment mechanisms range from stepped to stepless for different precision levels #adjustment
- [feature] Retention (grounds trapped in grinder) affects dose consistency and freshness #retention
- [price] Hand grinders offer excellent value, with models like Timemore C2 and 1Zpresso JX providing excellent results around $100-150 #budget
- [price] Entry-level electric burr grinders like Baratza Encore start around $170 but provide significant improvement over blade grinders #value
## Brewers
### Pour Over Brewers
- [equipment] Hario V60 uses a conical design with spiral ridges to control flow rate #pourover
- [equipment] Kalita Wave has a flat bottom with three small holes for more consistent extraction #pourover
- [equipment] Chemex combines brewer and server with thick proprietary filters for ultra-clean cup #pourover
- [material] Ceramic brewers retain heat better than plastic but are more fragile #materials
- [material] Glass brewers provide neutral flavor but less heat retention #materials
- [material] Plastic brewers are inexpensive, durable, and surprisingly good for heat retention #materials
### Immersion Brewers
- [equipment] French Press uses a metal mesh to separate grounds, allowing oils and fine particles to pass #immersion
- [equipment] AeroPress uses pressure and paper filter for clean, versatile brewing #immersion
- [equipment] Clever Dripper combines immersion and drip methods with a valve mechanism #hybrid
- [material] Glass French presses look elegant but break easily and have poor heat retention #materials
- [material] Stainless steel or ceramic French presses offer better durability and heat retention #materials
### Pressure Brewers
- [equipment] Espresso machines use 9 bars of pressure, requiring significant investment for good results #espresso
- [equipment] Moka pot uses steam pressure for strong, concentrated coffee at affordable price #moka
- [equipment] Manual lever machines like Flair or Robot provide espresso-style coffee with manual control #manual_espresso
## Kettles
- [equipment] Gooseneck kettles provide precision pouring control essential for pour over methods #kettles
- [feature] Variable temperature kettles allow precise temperature control for different roast levels #temp_control
- [feature] Flow restrictors can help beginners maintain consistent pour rates #pour_control
- [material] Electric kettles offer convenience and temperature stability #convenience
- [material] Stovetop kettles may be more durable but offer less temperature control #durability
## Accessories
- [equipment] Coffee scale with 0.1g precision helps maintain consistent ratios #measurement
- [equipment] Timer ensures consistent extraction times #consistency
- [equipment] Quality filters significantly impact flavor clarity and body #filters
- [equipment] Storage containers with one-way valves help preserve bean freshness #storage
- [equipment] Blind shaker or dosing cup reduces grinder mess and improves workflow #workflow
## Relations
- improves [[Coffee Brewing Methods]]
- affects [[Flavor Extraction]]
- requires [[Proper Maintenance]]
- enhances [[Home Coffee Setup]]
- part_of [[Coffee Knowledge Base]]
@@ -1,78 +0,0 @@
---
title: Coffee Bean Origins
type: note
permalink: coffee/coffee-bean-origins
tags:
- '#coffee'
- '#origins'
- '#beans'
- '#regions'
- '#demo'
---
# Coffee Bean Origins
An exploration of coffee-growing regions around the world and how geography, climate, and processing methods affect flavor profiles.
## Overview
Coffee beans are grown in various regions around the world, primarily in what's known as the "Coffee Belt" - the area between the Tropics of Cancer and Capricorn. The flavor characteristics of coffee beans are influenced by:
- Geographic region and climate
- Altitude
- Soil composition
- Variety of coffee plant
- Processing method
- Harvest and sorting practices
## Observations
- [principle] Higher altitude generally produces harder, denser beans with more complex acidity #altitude
- [region] Ethiopian beans often feature bright, fruity notes with floral aromatics #ethiopia
- [region] Colombian coffee typically offers balanced acidity with caramel sweetness and nutty undertones #colombia
- [region] Guatemalan coffee presents complex acidity with chocolate notes and sometimes spice characteristics #guatemala
- [region] Brazilian coffee tends toward nutty, chocolate notes with lower acidity and fuller body #brazil
- [region] Kenyan coffee is known for bright, wine-like acidity and berry or citrus notes #kenya
- [processing] Natural (dry) processing tends to create fruitier, more fermented flavors #processing
- [processing] Washed (wet) processing generally results in cleaner, brighter cups with more clarity #processing
- [processing] Honey processing creates a middle ground with some fruity notes while maintaining clarity #processing
- [factor] Shade-grown coffee typically develops more slowly, resulting in more complex flavors #cultivation
- [factor] Soil volcanic soil often imparts distinctive mineral characteristics to coffee #terroir
- [variety] Gesha/Geisha variety is known for exceptional floral and tea-like qualities #varieties
- [variety] Bourbon varieties often feature sweet, complex cup profiles #varieties
- [variety] Robusta beans have higher caffeine content but generally less complex flavor than Arabica #varieties
## Major Growing Regions
- [africa] Ethiopian coffees: Yirgacheffe, Sidamo, Harrar regions each with distinctive profiles #ethiopia
- [africa] Kenyan coffees: Often categorized by grade (AA, AB, etc.) based on bean size #kenya
- [americas] Colombian regions: Huila, Nariño, Antioquia each with unique characteristics #colombia
- [americas] Central American producers: Guatemala, Costa Rica, Panama known for balanced profiles #central_america
- [americas] Brazilian regions: Cerrado, Sul de Minas, Mogiana with varying profiles #brazil
- [asia] Indonesian islands: Sumatra, Java, Sulawesi producing earthy, full-bodied coffees #indonesia
- [asia] Vietnamese coffee: World's largest Robusta producer, often used in blends and commercial coffee #vietnam
## Processing Methods
- [natural] Beans dried inside the fruit, creating fruity, fermented notes and heavier body #processing
- [washed] Fruit removed before drying, resulting in cleaner cup with more pronounced acidity #processing
- [honey] Some fruit mucilage left on during drying, creates balanced sweetness and body #processing
- [wet-hulled] Unique to Indonesia, creates earthy, herbal, low-acid profiles #processing
- [experimental] Anaerobic fermentation, wine-yeast inoculation, and other newer methods #innovation
## Tasting Notes by Region
- [ethiopia] Blueberry, jasmine, bergamot, stone fruit, citrus #flavor_notes
- [kenya] Blackcurrant, tomato, tropical fruit, wine-like acidity #flavor_notes
- [colombia] Caramel, nuts, red apple, chocolate, balanced acidity #flavor_notes
- [guatemala] Chocolate, spice, green apple, balanced #flavor_notes
- [brazil] Nuts, chocolate, low acidity, full body #flavor_notes
- [indonesia] Earthy, herbal, spice, cedar, full body, low acidity #flavor_notes
## Relations
- influences [[Flavor Extraction]]
- pairs_with [[Coffee Brewing Methods]]
- affects [[Tasting Notes]]
- relates_to [[Specialty Coffee]]
- part_of [[Coffee Knowledge Base]]
@@ -1,70 +0,0 @@
---
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
- [method] Immersion methods like French press create fuller body and more rounded flavors #body
- [technique] Water at 195-205°F (90-96°C) extracts optimal flavor compounds for most brewing methods #temperature
- [technique] Grind size directly correlates with ideal extraction time (finer = shorter, coarser = longer) #grind
- [preference] Medium-light roasts often showcase more origin characteristics in pour over methods #roast
- [equipment] Burr grinders produce more consistent particle size than blade grinders, resulting in more even extraction #gear
- [ratio] 1:15 to 1:17 coffee-to-water ratio (by weight) works well for most brew methods #brewing
- [science] Different brewing temperatures extract different chemical compounds from the beans #chemistry
- [technique] Bloom phase (pre-infusion with small amount of water) allows CO2 to escape and improves extraction #bloom
## Pour Over Methods
- [method] V60 produces very clean cup with excellent clarity of flavor #pourover
- [method] Chemex uses thicker filter paper, resulting in even cleaner cup with fewer oils #pourover
- [method] Kalita Wave provides more consistent extraction due to flat bottom design #pourover
- [technique] Concentric circular pouring pattern ensures even saturation of grounds #technique
- [timing] Most pour over methods complete in 2:30-3:30 total brew time #brewing
## Immersion Methods
- [method] French Press creates full-bodied cup with rich mouthfeel due to metal filter allowing oils to pass #immersion
- [method] AeroPress is versatile, capable of producing both espresso-like and filter-style coffee #immersion
- [method] Cold brew uses time instead of heat to extract, resulting in lower acidity #immersion
- [technique] French press ideal steep time is 4-5 minutes before plunging #timing
- [technique] AeroPress inverted method prevents dripping during extraction phase #technique
## Pressure Methods
- [method] Espresso uses 9 bars of pressure to force water through finely ground coffee #pressure
- [method] Moka pot uses steam pressure to push water through grounds, creating strong, concentrated coffee #pressure
- [technique] Espresso requires very fine grind, almost powder-like consistency #grind
- [timing] Espresso shots typically extract in 25-30 seconds #timing
- [principle] Pressure methods can extract compounds that aren't soluble in regular brewing methods #extraction
## Relations
- requires [[Proper Grinding Technique]]
- affects [[Flavor Extraction]]
- pairs_with [[Coffee Bean Origins]]
- uses [[Brewing Equipment]]
- influences [[Tasting Notes]]
- part_of [[Coffee Knowledge Base]]
@@ -1,89 +0,0 @@
---
title: Coffee Flavor Map
type: note
permalink: coffee/coffee-flavor-map
tags:
- '#coffee'
- '#visualization'
- '#canvas'
- '#demo'
---
# Coffee Flavor Map
A visual mapping of coffee flavor attributes, brewing methods, and their relationships. This note describes a canvas visualization that could be generated to demonstrate Basic Memory's visualization capabilities.
## Overview
The Coffee Flavor Map provides a visual representation of how different brewing methods, coffee origins, and equipment choices affect flavor outcomes. This canvas visualization helps users understand the complex relationships in coffee brewing and tasting.
## Canvas Visualization Elements
### Core Nodes
- **Flavor Attributes**: Acidity, Sweetness, Body, Clarity, Bitterness, Complexity
- **Brewing Methods**: Pour Over, French Press, AeroPress, Espresso, Moka Pot, Cold Brew
- **Origin Regions**: Ethiopia, Kenya, Colombia, Brazil, Guatemala, Indonesia
- **Equipment Elements**: Grinder Quality, Water Temperature, Brewing Device, Filter Type
### Node Connections
- Lines connecting brewing methods to their typical flavor outcomes
- Arrows showing how equipment choices affect extraction variables
- Connections between origins and their characteristic flavor profiles
- Highlighting of optimal brewing methods for different origins
### Visual Organization
- Flavor outcomes in the center
- Brewing methods on the left side
- Origins on the right side
- Equipment variables at the bottom
- Color coding by category (methods, origins, equipment, flavors)
## Using This Visualization
### For Coffee Exploration
- Identify which brewing methods might highlight the characteristics you prefer
- See which origins naturally pair well with your preferred brewing method
- Understand how equipment changes can modify flavor outcomes
- Visualize the complex interplay between all coffee variables
### As a Basic Memory Demo
- Demonstrates Canvas visualization capabilities
- Shows how relations can be visually mapped
- Illustrates complex knowledge organization
- Provides an intuitive way to navigate coffee knowledge
## How To Generate This Canvas
In a conversation with Claude, you could request:
```
Please create a canvas visualization mapping the relationships between coffee brewing methods, origins, and flavor outcomes. Show how different equipment and techniques influence extraction and resulting flavor profiles.
```
This would generate a `.canvas` file in your Basic Memory directory that could be opened with Obsidian for an interactive visualization of these coffee relationships.
## Example Visualization Snippets
### Pour Over Method Node
- Connected to: High Clarity, Bright Acidity, Medium Body
- Best pairs with: Ethiopian and Kenyan beans
- Equipment dependencies: Gooseneck Kettle, Paper Filter, Burr Grinder
### Ethiopian Coffee Node
- Characteristic flavors: Floral, Fruity, Bright
- Best brewing methods: Pour Over, AeroPress
- Challenging with: French Press (loses clarity of delicate notes)
### Grind Size Node
- Affects: Extraction Rate, Flavor Balance
- Fine grind increases: Extraction Speed, Surface Area
- Coarse grind increases: Flow Rate, Reduces Bitter Compounds
## Relations
- visualizes [[Coffee Knowledge Base]]
- relates_to [[Coffee Brewing Methods]]
- relates_to [[Coffee Bean Origins]]
- relates_to [[Flavor Extraction]]
- relates_to [[Tasting Notes]]
- demonstrates [[Canvas]]
@@ -1,73 +0,0 @@
---
title: Coffee Knowledge Base
type: note
permalink: coffee/coffee-knowledge-base
tags:
- '#coffee'
- '#index'
- '#demo'
- '#knowledge'
---
# Coffee Knowledge Base
A comprehensive collection of coffee knowledge, from bean origins to brewing methods to tasting notes. This knowledge base demonstrates Basic Memory's ability to organize and connect information in a meaningful way.
## Overview
This Coffee Knowledge Base captures key information about coffee, structured with semantic observations and relations that connect different aspects of coffee knowledge. It serves as both a useful reference for coffee enthusiasts and a demonstration of how Basic Memory organizes information.
## Key Topics
### Core Coffee Knowledge
- [[Coffee Brewing Methods]] - Different techniques for preparing coffee
- [[Coffee Bean Origins]] - Where coffee comes from and how region affects flavor
- [[Brewing Equipment]] - Tools and devices used to prepare coffee
- [[Flavor Extraction]] - The science of dissolving flavor compounds from coffee
- [[Tasting Notes]] - How to taste and describe coffee flavors
### Brewing Techniques
- Proper grinding is fundamental to good extraction
- Water quality significantly impacts flavor
- Different brewing methods highlight different characteristics
- Time, temperature, and grind size are the key variables to control
- Freshness of beans dramatically affects quality
### Coffee Preferences
- Light roasts preserve more origin characteristics and acidity
- Dark roasts emphasize body and chocolatey/roasted flavors
- Pour over methods highlight clarity and distinct flavor notes
- Immersion methods create fuller body and rounded flavor
- Personal preference matters more than "correctness"
## Using This Knowledge Base
### For Learning
Use this knowledge base to:
- Understand coffee fundamentals
- Explore connections between brewing methods and flavor outcomes
- Learn how different origins produce distinct flavor profiles
- Discover how equipment affects the brewing process
- Develop a vocabulary for describing coffee experiences
### As a Demo
This knowledge base demonstrates:
- Semantic knowledge organization with categories and relations
- Building connections between related concepts
- Creating a navigable knowledge graph
- Structuring information in a way both humans and AI assistants can understand
- How Basic Memory enables persistent knowledge across conversations
## Relations
- contains [[Coffee Brewing Methods]]
- contains [[Coffee Bean Origins]]
- contains [[Brewing Equipment]]
- contains [[Flavor Extraction]]
- contains [[Tasting Notes]]
- demonstrates [[Basic Memory Capabilities]]
@@ -1,79 +0,0 @@
---
title: Flavor Extraction
type: note
permalink: coffee/flavor-extraction
tags:
- '#coffee'
- '#extraction'
- '#brewing'
- '#science'
- '#demo'
---
# Flavor Extraction
Understanding the science of coffee extraction, how different compounds dissolve at different rates, and how to control extraction to achieve desired flavor profiles.
## Overview
Coffee extraction is the process of dissolving flavor compounds from ground coffee into water. The science of extraction is key to producing a balanced, flavorful cup. Extraction is affected by numerous variables including grind size, water temperature, contact time, agitation, and pressure.
## Observations
- [science] Coffee contains over 1,000 aroma compounds and hundreds of flavor compounds #chemistry
- [principle] Extraction occurs in a predictable sequence: acids → sugars → bitter compounds #extraction_order
- [principle] Under-extraction results in sour, bright, thin coffee lacking sweetness and body #under_extraction
- [principle] Over-extraction results in bitter, hollow, astringent flavors #over_extraction
- [principle] The goal is typically balanced extraction (18-22% of coffee solubles dissolved) #balanced_extraction
- [technique] Finer grind size increases extraction rate due to greater surface area #grind_size
- [technique] Higher water temperature increases extraction rate and solubility of compounds #temperature
- [technique] Longer contact time allows more complete extraction #brew_time
- [technique] Agitation (stirring, turbulence) increases extraction rate by preventing saturation zones #agitation
- [technique] Pressure (as in espresso) can extract compounds that aren't water-soluble at atmospheric pressure #pressure
## Factors Affecting Extraction
- [factor] Grind size: Finer = faster extraction, coarser = slower extraction #grind
- [factor] Water temperature: Higher = faster extraction, lower = slower extraction #temperature
- [factor] Contact time: Longer = more extraction, shorter = less extraction #time
- [factor] Agitation: More = faster extraction, less = slower extraction #agitation
- [factor] Coffee-to-water ratio: More coffee = lower extraction percentage #ratio
- [factor] Water quality: Mineral content affects extraction of different compounds #water
- [factor] Roast level: Darker roasts extract more easily than lighter roasts #roast
- [factor] Bean density: Denser beans (typically high-altitude) require more effort to extract #density
- [factor] Freshness: Freshly roasted coffee extracts differently than aged coffee #freshness
- [factor] Brewing method: Different methods extract different compounds at different rates #method
## Signs of Extraction Levels
- [under] Sour, bright, lack of sweetness, thin body, quick finish #flavor
- [under] Typically from: too coarse grind, too cool water, too short brew time #causes
- [balanced] Sweet, bright but not sour, rich but not bitter, pleasing finish #flavor
- [balanced] Achieved through proper ratio of variables for given coffee #technique
- [over] Bitter, hollow, astringent, dry finish, sometimes papery #flavor
- [over] Typically from: too fine grind, too hot water, too long brew time #causes
## Measuring Extraction
- [method] Total Dissolved Solids (TDS) meters measure concentration of coffee solution #measurement
- [method] Extraction yield = percentage of coffee grounds dissolved in the final brew #calculation
- [preference] Specialty coffee typically targets 18-22% extraction yield #standards
- [preference] Some specialty light roasts may taste best at higher extraction percentages #speciality
## Controlling Extraction
- [technique] Adjust grind size as primary extraction control #basics
- [technique] Use water temperature to fine-tune extraction #fine_tuning
- [technique] Modify pour technique to control agitation level #technique
- [technique] Adjust coffee-to-water ratio to balance strength and extraction #ratio
- [technique] Pre-infusion (blooming) helps achieve even extraction #blooming
- [technique] Pulse pouring creates different extraction dynamics than continuous pour #pour_technique
## Relations
- affected_by [[Coffee Brewing Methods]]
- influenced_by [[Coffee Bean Origins]]
- enhanced_by [[Brewing Equipment]]
- determines [[Tasting Notes]]
- requires [[Water Quality]]
- part_of [[Coffee Knowledge Base]]
@@ -1,161 +0,0 @@
{
"nodes":[
{
"id":"node-5",
"type":"text",
"text":"## Main Pour Phase\n- Use concentric circles from center outward\n- Maintain steady, controlled flow rate\n- Avoid pouring directly on filter walls\n- Keep water level consistent\n- Pulse pour in 2-3 stages (or continuous pour)\n- Total brew time target: 2:30-3:30",
"position":{"x":450,"y":200},
"x":530,
"y":-100,
"width":300,
"height":200,
"color":"1"
},
{
"id":"node-8",
"type":"text",
"text":"## Drawdown\n- Allow water to fully drain\n- Flat bed indicates even extraction\n- Total brew time should be ~2:30-3:30\n- Remove filter promptly after brewing",
"position":{"x":450,"y":700},
"x":540,
"y":375,
"width":300,
"height":150,
"color":"1"
},
{
"id":"node-6",
"type":"text",
"text":"## Pour Pattern\n\nConcentric circles ensure even saturation of coffee grounds. Begin at the center and work outward, avoiding filter edges. Pour height of 1-2 inches above coffee bed.",
"position":{"x":250,"y":450},
"x":960,
"y":25,
"width":300,
"height":150,
"color":"5"
},
{
"id":"node-12",
"type":"text",
"text":"## Tasting Notes\n\n- Balanced extraction: sweet, bright, complex\n- Under-extraction: sour, lacking sweetness\n- Over-extraction: bitter, astringent, hollow\n\nTake notes on each brew to track improvements and preferences.",
"position":{"x":-250,"y":700},
"x":1020,
"y":420,
"width":300,
"height":150,
"color":"6"
},
{
"id":"node-9",
"type":"text",
"text":"## Troubleshooting\n\n- Too sour/weak: Grind finer, water hotter, increase brew time\n- Too bitter/strong: Grind coarser, water cooler, decrease brew time\n- Uneven extraction: Improve pour technique, better grinder\n- Channeling: More careful pouring, better bloom\n- Slow drawdown: Coarser grind, less agitation\n- Fast drawdown: Finer grind, more careful pouring",
"position":{"x":100,"y":700},
"x":30,
"y":570,
"width":300,
"height":200,
"color":"6"
},
{
"id":"node-3",
"type":"text",
"text":"## Preparation\n- Heat water to 195-205°F (90-96°C)\n- Measure coffee (1:15 to 1:17 ratio)\n- Medium-fine grind (sea salt consistency)\n- Rinse filter with hot water\n- Discard rinse water\n- Add ground coffee to filter\n- Level coffee bed",
"position":{"x":-250,"y":200},
"x":30,
"y":-500,
"width":300,
"height":200,
"color":"3"
},
{
"id":"node-1",
"type":"text",
"text":"# Perfect Pour Over Method\n\nA systematic approach to brewing exceptional pour over coffee by controlling key variables and following proper technique.",
"position":{"x":0,"y":0},
"x":-580,
"y":-760,
"width":400,
"height":120,
"color":"4"
},
{
"id":"node-10",
"type":"text",
"text":"## Grinding Parameters\n\n- V60: Medium-fine (sea salt)\n- Chemex: Medium (slightly coarser than V60)\n- Kalita Wave: Medium (between V60 and Chemex)\n\nConsistent particle size is critical; use quality burr grinder.",
"position":{"x":-250,"y":450},
"x":30,
"y":-910,
"width":300,
"height":150,
"color":"5"
},
{
"id":"node-2",
"type":"text",
"text":"## Equipment Setup\n- Clean V60/Chemex/Kalita Wave\n- Paper filter (rinsed)\n- Server/mug\n- Scale with timer\n- Gooseneck kettle\n- Burr grinder\n- Fresh coffee beans",
"position":{"x":-600,"y":200},
"x":-530,
"y":-500,
"width":300,
"height":200,
"color":"3"
},
{
"id":"node-4",
"type":"text",
"text":"## The Bloom\n- Start timer\n- Pour 2-3x coffee weight water\n- Ensure all grounds are saturated\n- Gentle stir or swirl if needed\n- Allow 30-45 seconds for degassing\n- Look for bubbling and dome formation",
"position":{"x":100,"y":200},
"x":530,
"y":-500,
"width":300,
"height":200,
"color":"1"
},
{
"id":"node-13",
"type":"text",
"text":"## Coffee-to-Water Ratio\n\n- Standard: 1:15 to 1:17 (coffee:water)\n- Stronger cup: 1:15 (67g/L)\n- Medium cup: 1:16 (62.5g/L)\n- Lighter cup: 1:17 (58.8g/L)\n\nExample: For 300ml water, use ~18-20g coffee",
"position":{"x":-600,"y":700},
"x":30,
"y":-100,
"width":300,
"height":150,
"color":"6"
},
{
"id":"node-7",
"type":"text",
"text":"## Brew Time Guideline\n\n- Bloom: 30-45 seconds\n- First pour: 1:00-1:15\n- Second pour: 1:45-2:00\n- Final pour: 2:15-2:30\n- Drawdown complete: 2:45-3:30\n\nAdjust for taste: shorter for lighter, longer for stronger",
"position":{"x":600,"y":450},
"x":-80,
"y":220,
"width":300,
"height":200,
"color":"5"
},
{
"id":"node-11",
"type":"text",
"text":"## Water Quality\n\n- Clean, filtered water\n- No strong odors or flavors\n- Ideal TDS: 75-150 ppm\n- Ideal pH: 7.0-7.5\n- Avoid distilled water (lacks minerals)\n- Avoid hard water (scaling issues)",
"position":{"x":-600,"y":450},
"x":-780,
"y":-125,
"width":300,
"height":150,
"color":"5"
}
],
"edges":[
{"id":"edge-1","fromNode":"node-1","fromSide":"bottom","toNode":"node-2","toSide":"top","label":"Step 1"},
{"id":"edge-2","fromNode":"node-2","fromSide":"right","toNode":"node-3","toSide":"left","label":"Step 2"},
{"id":"edge-3","fromNode":"node-3","fromSide":"right","toNode":"node-4","toSide":"left","label":"Step 3"},
{"id":"edge-4","fromNode":"node-4","fromSide":"bottom","toNode":"node-5","toSide":"top","label":"Step 4"},
{"id":"edge-5","fromNode":"node-5","fromSide":"bottom","toNode":"node-8","toSide":"top","label":"Step 5"},
{"id":"edge-6","fromNode":"node-5","fromSide":"left","toNode":"node-7","toSide":"right","label":"Timing"},
{"id":"edge-7","fromNode":"node-5","fromSide":"right","toNode":"node-6","toSide":"left","label":"Technique"},
{"id":"edge-8","fromNode":"node-8","fromSide":"left","toNode":"node-9","toSide":"right","label":"if problems"},
{"id":"edge-9","fromNode":"node-3","fromSide":"top","toNode":"node-10","toSide":"bottom","label":"Grinding details"},
{"id":"edge-10","fromNode":"node-2","fromSide":"bottom","toNode":"node-11","toSide":"right","label":"Water details"},
{"id":"edge-11","fromNode":"node-8","fromSide":"right","toNode":"node-12","toSide":"left","label":"Evaluate"},
{"id":"edge-12","fromNode":"node-3","fromSide":"bottom","toNode":"node-13","toSide":"top","label":"Ratio details"}
]
}
-84
View File
@@ -1,84 +0,0 @@
---
title: Tasting Notes
type: note
permalink: coffee/tasting-notes
tags:
- '#coffee'
- '#tasting'
- '#flavor'
- '#cupping'
- '#demo'
---
# Tasting Notes
How to taste and evaluate coffee, identify flavor characteristics, and develop a personal coffee palate.
## Overview
Coffee tasting, or "cupping" in professional contexts, is the practice of observing the tastes and aromas of brewed coffee. Developing a coffee palate helps identify preferences, communicate about coffee experiences, and better understand how brewing variables affect the cup.
## Observations
- [principle] Flavor perception includes taste, aroma, mouthfeel, and retronasal perception #sensory
- [principle] Our taste buds can only perceive sweet, sour, salty, bitter, and umami #taste
- [principle] Most of what we call "flavor" is actually aroma detected retronasally #aroma
- [technique] Professional coffee tasting (cupping) uses a standardized protocol for consistency #cupping
- [technique] Slurping coffee aerates it and spreads it across all taste receptors #technique
- [technique] Allowing coffee to cool reveals different flavor notes at different temperatures #temperature
## Coffee Flavor Wheel
- [tool] The SCA Coffee Flavor Wheel provides a standardized vocabulary for describing coffee #flavor_wheel
- [category] Primary categories include: Fruity, Floral, Sweet, Nutty/Cocoa, Spice, Roasted, Other #categories
- [subcategory] Fruity breaks down into: Berry, Dried Fruit, Citrus Fruit, Stone Fruit, Tropical Fruit, etc. #fruit_notes
- [subcategory] Floral includes: Floral, Black Tea, Chamomile, Rose, Jasmine, etc. #floral_notes
- [subcategory] Sweet includes: Brown Sugar, Molasses, Honey, Maple Syrup, Vanilla, etc. #sweet_notes
- [subcategory] Nutty/Cocoa includes: Nut, Cocoa, Dark Chocolate, Chocolate, etc. #nutty_notes
- [subcategory] Spice includes: Brown Spice, Pepper, Anise, Nutmeg, Cinnamon, etc. #spice_notes
## Basic Tasting Components
- [component] Acidity: The bright, tangy quality (not sourness from under-extraction) #acidity
- [component] Sweetness: The pleasant, sugary quality balancing other elements #sweetness
- [component] Body: The physical mouthfeel and weight of the coffee #body
- [component] Finish/Aftertaste: The flavor that lingers after swallowing #finish
- [component] Balance: How well all elements work together #balance
- [component] Complexity: The range and layers of distinct flavors #complexity
- [component] Cleanliness: Absence of defects or off-flavors #cleanliness
## Common Flavor Notes by Origin
- [ethiopia] Blueberry, jasmine, bergamot, lemon, tea-like #flavor_notes
- [kenya] Blackcurrant, grapefruit, tomato-like acidity, winey #flavor_notes
- [colombia] Caramel, red apple, nuts, chocolate, balanced acidity #flavor_notes
- [guatemala] Chocolate, spice, apple, medium acidity #flavor_notes
- [brazil] Nuts, chocolate, low-to-medium acidity, full body #flavor_notes
- [indonesia] Earthy, herbal, spice, cedar, full body, low acidity #flavor_notes
- [costa_rica] Clean, bright, citrus, balanced, light chocolate #flavor_notes
## Developing Your Palate
- [technique] Taste coffees side-by-side to identify differences #comparison
- [technique] Try describing flavors before looking at roaster's notes #blind_tasting
- [technique] Keep a coffee journal with detailed notes about each coffee #journaling
- [technique] Explore different processing methods of the same origin #processing
- [technique] Try the same coffee brewed with different methods #brewing_comparison
- [technique] Use reference flavors (actual fruits, chocolates, etc.) to calibrate your palate #calibration
## Personal Coffee Experiences
- [experience] Ethiopian Yirgacheffe prepared as pour over: intense blueberry, jasmine aromatics, tea-like body
- [experience] Sumatra Mandheling in French press: earthy, cedar, herbal, tobacco, full body
- [experience] Panama Gesha as pour over: intense floral notes, jasmine, bergamot, delicate body
- [experience] Brazil Cerrado as espresso: nutty, chocolate, caramel, low acidity, great crema
- [experience] Kenya AA as pour over: bright blackcurrant, tomato-like acidity, winey finish
## Relations
- determined_by [[Flavor Extraction]]
- influenced_by [[Coffee Bean Origins]]
- varies_with [[Coffee Brewing Methods]]
- enhanced_by [[Proper Grinding Technique]]
- documented_in [[Coffee Journal]]
- part_of [[Coffee Knowledge Base]]
@@ -1,69 +0,0 @@
---
title: Test Note Creation - Basic Functionality
type: note
permalink: testing/test-note-creation-basic-functionality
tags:
- '["testing"'
- '"core-functionality"'
- '"note-creation"]'
---
---
title: Test Note Creation - Basic Functionality
tags: [testing, core-functionality, note-creation, edited]
test_status: active
last_edited: 2025-06-01
---
# Test Note Creation - Basic Functionality
## Test Status: COMPREHENSIVE TESTING IN PROGRESS
Testing basic note creation with various content types and structures.
## Content Types Tested
- Plain text content ✓
- Markdown formatting **bold**, *italic*
- Lists:
- Bullet points
- Numbered items
- Code blocks: `inline code`
```python
# Block code
def test_function():
return "Hello, Basic Memory!"
```
## Special Characters
- Unicode: café, naïve, résumé
- Emojis: 🚀 🔬 📝
- Symbols: @#$%^&*()
## Frontmatter Testing
This note should have proper frontmatter parsing.
## Relations to Test
- connects_to [[Another Test Note]]
- validates [[Core Functionality Tests]]
## Observations
- [success] Note creation initiated
- [test] Content variety included
- [validation] Special characters included
## Edit Test Results
- [success] Note reading via title lookup ✓
- [success] Search functionality returns relevant results ✓
- [success] Special characters (unicode, emojis) preserved ✓
- [test] Now testing append edit operation ✓
## Performance Notes
- Note creation: Instantaneous
- Note reading: Fast response
- Search: Good relevance scoring
## Next Tests
- Edit operations (append, prepend, find_replace)
- Move operations
- Cross-project functionality
-128
View File
@@ -1,128 +0,0 @@
# Basic Memory Installation Guide for LLMs
This guide is specifically designed to help AI assistants like Cline install and configure Basic Memory. Follow these
steps in order.
## Installation Steps
### 1. Install Basic Memory Package
Use one of the following package managers to install:
```bash
# Install with uv (recommended)
uv tool install basic-memory
# Or with pip
pip install basic-memory
```
### 2. Configure MCP Server
Add the following to your config:
```json
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": [
"basic-memory",
"mcp"
]
}
}
}
```
For Claude Desktop, this file is located at:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
### 3. Start Synchronization (optional)
To synchronize files in real-time, run:
```bash
basic-memory sync --watch
```
Or for a one-time sync:
```bash
basic-memory sync
```
## Configuration Options
### Custom Directory
To use a directory other than the default `~/basic-memory`:
```bash
basic-memory project add custom-project /path/to/your/directory
basic-memory project default custom-project
```
### Multiple Projects
To manage multiple knowledge bases:
```bash
# List all projects
basic-memory project list
# Add a new project
basic-memory project add work ~/work-basic-memory
# Set default project
basic-memory project default work
```
## Importing Existing Data
### From Claude.ai
```bash
basic-memory import claude conversations path/to/conversations.json
basic-memory import claude projects path/to/projects.json
```
### From ChatGPT
```bash
basic-memory import chatgpt path/to/conversations.json
```
### From MCP Memory Server
```bash
basic-memory import memory-json path/to/memory.json
```
## Troubleshooting
If you encounter issues:
1. Check that Basic Memory is properly installed:
```bash
basic-memory --version
```
2. Verify the sync process is running:
```bash
ps aux | grep basic-memory
```
3. Check sync output for errors:
```bash
basic-memory sync --verbose
```
4. Check log output:
```bash
cat ~/.basic-memory/basic-memory.log
```
For more detailed information, refer to the [full documentation](https://memory.basicmachines.co/).
-378
View File
@@ -1,378 +0,0 @@
{"type":"entity","name":"Paul","entityType":"person","observations":["Software developer combining DIY ethics, Free Software principles, and theoretical computer science","Created the Basic Machines project","Values authentic exchange of ideas","Approaches AI interaction with emphasis on genuine technical discussion","Comfortable with uncertainty and open dialogue","Balances practical implementation with broader implications"]}
{"type":"entity","name":"Basic_Machines","entityType":"project","observations":["Local-first knowledge management system","Combines filesystem durability with graph-based knowledge representation","Focuses on enhancing human agency and understanding","Synthesizes DIY ethics, Free Software philosophy, and theoretical computer science","Current focus includes basic-memory system"]}
{"type":"entity","name":"basic-memory","entityType":"software_system","observations":["A core component of Basic Machines","Local-first knowledge management system","Combines filesystem persistence with graph-based knowledge representation","Being implemented collaboratively by Paul and Claude"]}
{"type":"entity","name":"basic-memory_implementation_patterns","entityType":"technical_patterns","observations":["Filesystem is source of truth - all changes write to files first","Clean separation of concerns between models (SQLAlchemy), schemas (Pydantic), and services","Repository pattern for database access","Service layer handling business logic and coordination","Atomic file operations using temporary files for safety","Clear error handling hierarchy with specific error types","Comprehensive test coverage with pytest and fixtures","Async/await used throughout the codebase","Validation using Pydantic models with custom validators"]}
{"type":"entity","name":"fileio_module","entityType":"code_module","observations":["Extracted from EntityService to handle all file operations","Provides read_entity_file, write_entity_file, and delete_entity_file functions","Handles markdown parsing and formatting","Implements atomic file operations","Provides consistent error handling","Enables reuse across services"]}
{"type":"entity","name":"entity_service","entityType":"code_module","observations":["Manages entities in both filesystem and database","Uses fileio module for file operations","Maintains database index of entities","Handles entity creation, retrieval, and deletion","Follows 'filesystem is source of truth' principle","Coordinates with observation service for full entity management"]}
{"type":"entity","name":"observation_service","entityType":"code_module","observations":["Manages observations within entity files","Provides database indexing for efficient observation queries","Works with complete Entity objects rather than IDs","Handles observation addition and search","Maintains consistency between files and database","Under development for update/remove operations"]}
{"type":"entity","name":"observation_management","entityType":"design_challenge","observations":["Key challenge: maintaining observation state across files and database","Exploring bulk update approach - treating all observations as a unit","Considering tracked observations with markdown comments for IDs","Investigating diff-based approach for observation-level changes","Evaluating position-based management without explicit IDs","Trade-offs between implementation complexity and markdown readability"]}
{"type":"entity","name":"testing_infrastructure","entityType":"technical_patterns","observations":["Uses pytest with async support via pytest-asyncio","In-memory SQLite database for test isolation","Temporary directories for file operation testing","Comprehensive fixture system for test setup","Tests organized by component (entity, observation, etc)","Covers happy path, error cases, and edge cases","Uses monkeypatch for mocking dependencies","Clear separation between arrange, act, assert sections","Uses in-memory SQLite database for test isolation","Comprehensive fixture system for test data setup","Proper async test handling with pytest-asyncio"]}
{"type":"entity","name":"test_categories","entityType":"test_suite","observations":["Happy path tests verify core functionality","Error path tests ensure proper error handling","Edge cases test special characters and long content","File operation tests verify atomic writes and rollbacks","Database sync tests verify index consistency","Recovery tests for rebuild operations","Punted on concurrent operation tests due to session management complexity"]}
{"type":"entity","name":"completed_work","entityType":"project_milestone","observations":["Extracted file operations to fileio.py module","Updated EntityService to use fileio functions","Implemented initial ObservationService","Created comprehensive test suite","Established clear project patterns and principles","Set up basic database schema with SQLAlchemy","Created Pydantic models for validation"]}
{"type":"entity","name":"future_work","entityType":"project_tasks","observations":["Implement observation updates/removals","Design proper session management for concurrent operations","Update EntityService tests for new fileio module","Add more sophisticated search functionality","Handle markdown formatting edge cases","Consider versioning for file changes","Implement proper backup strategy"]}
{"type":"entity","name":"design_decisions","entityType":"technical_decisions","observations":["Filesystem as source of truth over database","Markdown format for human readability and editing","Atomic file operations for safety","SQLite + SQLAlchemy for proven reliability","Pydantic for validation and ID generation","Async/await for better scalability","Clear separation between files and database roles","Explicit error hierarchies for better handling"]}
{"type":"entity","name":"concurrency_considerations","entityType":"technical_challenge","observations":["SQLAlchemy session management in async context","File operation atomicity","Transaction isolation levels","Potential for conflicting updates","Need for proper session lifecycle","Possibility of file system race conditions","Database lock management"]}
{"type":"entity","name":"observation_update_approaches","entityType":"design_alternatives","observations":["Each approach trades off between simplicity, efficiency, and robustness","Four main approaches considered: bulk update, tracked IDs, diff-based, and position-based","Discussion revealed importance of human readability in file format","Consideration of manual editing workflows key to design","File system as source of truth principle guides tradeoffs"]}
{"type":"entity","name":"bulk_update_approach","entityType":"design_option","observations":["Update all observations at once in a single operation","Simpler file operations - just rewrite the whole list","No need for observation matching or IDs","Very consistent with source of truth principle","Less efficient for small changes","May have concurrency implications","Simplest implementation option"]}
{"type":"entity","name":"tracked_observations_approach","entityType":"design_option","observations":["Use markdown comments to store observation IDs","Enables precise updates and deletes","IDs stored as HTML comments in markdown","More complex markdown parsing required","IDs visible in raw markdown files","Balances tracking with readability"]}
{"type":"entity","name":"diff_based_approach","entityType":"design_option","observations":["Implement observation-aware diffing","Track changes at observation level","More efficient for updates","Preserves manual edits and changes","More complex implementation needed","Must handle merge conflicts","Most sophisticated option considered"]}
{"type":"entity","name":"position_based_approach","entityType":"design_option","observations":["Track observations by position/order","No explicit IDs needed","Cleanest markdown format","Order changes could break references","Difficult to handle concurrent edits","Most fragile option considered"]}
{"type":"entity","name":"tasks_and_progress","entityType":"project_tracking","observations":["Current focus on observation management implementation","Completed core file operations extraction","Completed EntityService updates","Completed initial ObservationService","Basic test coverage in place","Future work includes concurrent operations","Future work includes search improvements","Need to handle markdown edge cases"]}
{"type":"entity","name":"error_handling_patterns","entityType":"technical_patterns","observations":["Custom exception hierarchy with ServiceError base","Specific error types (FileOperationError, DatabaseSyncError, etc)","Clear separation between file and database errors","Error propagation patterns established","Focus on actionable error messages","Error handling at appropriate levels"]}
{"type":"entity","name":"data_models","entityType":"technical_implementation","observations":["SQLAlchemy models for database structure","Pydantic schemas for API/service layer","Entity model with UUID-based IDs","Observation model with entity relationships","UTCDateTime custom type for timestamps","Automatic ID generation in Pydantic models","Strict validation rules"]}
{"type":"entity","name":"markdown_format","entityType":"file_format","observations":["Simple, human-readable format","Entity name as H1 header","Metadata in key-value format","Observations as bullet points","Atomic file operations for updates","Designed for manual editing","No hidden metadata in main content"]}
{"type":"entity","name":"test_driven_development","entityType":"development_pattern","observations":["Tests revealed need for atomic file operations","Error cases drove error hierarchy design","Edge cases informed validation rules","Test fixtures shaped service interfaces","File operations extracted due to test patterns","Concurrent test issues revealed session management needs"]}
{"type":"entity","name":"architecture_evolution","entityType":"design_process","observations":["Started with simple EntityService implementation","Circular dependency between Entity and Observation services revealed design flaw","Extracted file operations to separate module","Moved to passing Entity objects rather than IDs","Improved separation of concerns through iterations","File operations became reusable across services","Database became true 'index' rather than source of truth"]}
{"type":"entity","name":"validation_patterns","entityType":"technical_patterns","observations":["Pydantic models provide schema validation","Automatic ID generation if not provided","Database constraints via SQLAlchemy","Runtime checks in services","Markdown format validation","Error handling for invalid states"]}
{"type":"entity","name":"markdown_examples","entityType":"documentation","observations":["Example of basic entity:\n# Entity Name\ntype: entity_type\n\n## Observations\n- First observation\n- Second observation","Example with special characters:\n# Test & Entity!\ntype: test\n\n## Observations\n- Test & observation with @#$% special chars!","Format ensures human readability:\n# Basic Machines\ntype: project\n\n## Observations\n- Local-first knowledge management system\n- Combines filesystem durability with graph-based knowledge representation","Future consideration for observation IDs:\n# Entity Name\ntype: entity_type\n\n## Observations\n- <!-- obs-id: abc123 -->\n This is an observation with ID"]}
{"type":"entity","name":"markdown_parsing_rules","entityType":"technical_implementation","observations":["H1 header contains entity name","Metadata uses key: value format","Observations section marked by H2 header","Each observation is a markdown list item","Blank lines separate sections","Special characters allowed in content","No restrictions on observation content"]}
{"type":"entity","name":"schema_definitions","entityType":"technical_documentation","observations":["SQLAlchemy Entity model:\nclass Entity(Base):\n id: str (primary key)\n name: str (unique)\n entity_type: str\n created_at: datetime\n updated_at: datetime","SQLAlchemy Observation model:\nclass Observation(Base):\n id: str (primary key)\n entity_id: str (foreign key)\n content: str\n created_at: datetime\n context: Optional[str]","Pydantic Entity schema:\nclass Entity(BaseModel):\n id: str\n name: str\n entity_type: str\n observations: List[Observation]"]}
{"type":"entity","name":"test_evolution","entityType":"development_history","observations":["Started with basic Entity CRUD tests","Added filesystem verification to all tests","Developed concurrent operation tests (later removed)","Edge case tests drove better error handling","Test fixtures evolved to support both file and DB testing","Mocking patterns for file/DB operations","Special cases for long content and special characters"]}
{"type":"entity","name":"implementation_challenges","entityType":"technical_issues","observations":["Initial circular dependency between services","SQLAlchemy session management in async context","Atomic file operations with proper error handling","Maintaining DB sync with filesystem changes","Handling long content in observations","Managing test isolation with file operations","Deciding on markdown format tradeoffs","Concurrent operation complexity"]}
{"type":"entity","name":"Basic_Factory","entityType":"Project","observations":["Collaborative project between Paul and Claude","Explores AI-human collaboration in software development","Uses MCP tools for file and memory management","Built with git integration capabilities","Focuses on maintaining project context across sessions","About 90% complete with MCP tools","Still needs improvements in collaboration via files/git/github","Will be used to document and share collaborative development process"]}
{"type":"entity","name":"Basic_Factory_Components","entityType":"Technical","observations":["Server-side rendering with JinjaX","HTMX for dynamic updates","Alpine.js for client-side state","Tailwind CSS for styling","Component translation from React/shadcn/ui","Focus on simplicity and understandability","Demonstrates meta-compiler principles in component translation"]}
{"type":"entity","name":"Component_Translation_Process","entityType":"Methodology","observations":["Treats component porting as meta-compilation","Maps between React/TypeScript and JinjaX/Alpine.js domains","Uses formal grammar transformation approaches","Maintains functionality while simplifying implementation","Focuses on server-side rendering patterns","Preserves accessibility and performance","Uses short, focused git branches for each component"]}
{"type":"entity","name":"Basic_Machines_Philosophy","entityType":"Philosophy","observations":["Combines DIY punk ethics with software development","Emphasizes user empowerment and understanding","Values simplicity and composability","Treats complex systems as combinations of simple parts","Focuses on authentic creation and sharing","Draws inspiration from punk rock, Free Software, and theoretical CS","Emphasizes the cycle of creation, complexity, and renewal"]}
{"type":"entity","name":"Basic_Machines_Manifesto","entityType":"Document","observations":["Created through collaboration between Paul and Claude","Explores connection between DIY punk ethics and software development","Emphasizes composition over inheritance in both philosophy and practice","Views software development through lens of basic machines that combine for complex computation","Advocates for user empowerment and technological independence","Structured in sections covering Origins, Philosophy, Technical Implementation, and AI Collaboration","Draws connections between punk rock, free software, and theoretical computer science","Emphasizes importance of sharing knowledge and building community","Released in December 2024"]}
{"type":"entity","name":"AI_Human_Collaboration_Model","entityType":"Methodology","observations":["Focuses on deep collaboration rather than simple task completion","Maintains rich context across sessions via knowledge graph","Uses short, focused git branches for each collaborative session","Values intellectual partnership over simple code generation","Emphasizes both practical implementation and theoretical exploration","Creates space for authentic exchange while maintaining AI/human clarity","Uses formal methods when appropriate (like grammar transformation)","Documents decisions and processes for future reference","Developed through Basic Machines project experience"]}
{"type":"entity","name":"Basic_Machines_Roadmap","entityType":"Project_Plan","observations":["Phase 1 (30 days): Build basic-machines.co website","Phase 2 (60-90 days): Develop premium component bundles","Phase 3 (90-120 days): Launch Basic Foundation commercial offering","Focus on building brand and marketing presence","Prioritize components needed for own site development","Document and share collaboration process","Build sustainable business model aligned with values"]}
{"type":"entity","name":"Basic_Machines_Website","entityType":"Project","observations":["To be built at basic-machines.co","Will showcase products and vision","Needs components for navigation, hero sections, features","Will demonstrate component usage in production","Will include blog for sharing progress","Focus on clear value proposition","Platform for sharing Basic Machines philosophy"]}
{"type":"entity","name":"Basic_Memory_Markdown_Example","entityType":"Example","observations":["Shows complete markdown structure for basic-memory entity","Uses frontmatter for metadata (id, type, created, context)","Has main description section after title","Includes Observations as bullet points","Shows Relations with [id] relation_type | context format","Lists References at bottom","Created during initial design discussion","Serves as canonical example of file format"]}
{"type":"entity","name":"Basic_Memory_Database_Schema","entityType":"Technical","observations":["Uses SQLite for local storage","Entities table with id, name, type, created_at, context, description, references","Observations table linking to entities with content and context","Relations table tracking directional relationships between entities","References column needs quotes as SQL reserved word","Designed for easy rebuilding from markdown files","Foreign key constraints maintain data integrity","Unique constraint on relations prevents duplicates","Created_at timestamps track history","Context fields enable tracking information sources"]}
{"type":"entity","name":"Basic_Memory_Project_Structure","entityType":"Technical","observations":["Uses dbmate for database migrations","Projects directory stores SQLite databases and markdown files","Makefile provides common development commands","Environment vars configure database connection","db/migrations directory for SQL schema changes","Gitignore excludes database files and env config","Uses Python 3.12 with modern tooling","Tests directory for pytest files","Follows Basic Machines project conventions"]}
{"type":"entity","name":"Basic_Memory_Project_Isolation_Decision","entityType":"Decision","observations":["Decided to defer multi-project support to post-MVP","Will use separate SQLite databases per project","Initially using projects directory in code repository","Plan to make location configurable later","No changes needed to core domain model","Keeps initial implementation simple","FTS/search capabilities also deferred for simplicity"]}
{"type":"entity","name":"Basic_Memory_Implementation_Plan","entityType":"Plan","observations":["Start with SQLAlchemy models matching schema","Then build CLI for basic operations","Then implement markdown parser","Use TDD approach throughout","Begin with core domain model","CLI will support CRUD operations","Parser must handle frontmatter and sections","Following modular development approach","Planning to use typer for CLI","Will use modern Python tools and practices"]}
{"type":"entity","name":"Basic_Memory_Implementation_Status","entityType":"Status","observations":["Core modules implemented: models, services, repository, fileio","Modular architecture with clear separation of concerns","File operations extracted to separate fileio module","Initial ObservationService implementation complete","Basic test coverage in place","Exploring observation management strategies","Using SQLAlchemy for database interaction","Markdown file operations working","Entity management functional","Repository layer implementation complete with SQLAlchemy models and tests","Database operations working with proper UTC timestamp handling","In-memory SQLite testing infrastructure proven effective"]}
{"type":"entity","name":"Basic_Memory_Observation_Management_Design","entityType":"Design","observations":["Four approaches under consideration","Bulk Update: Simple but less efficient","Tracked Observations: Precise but clutters markdown","Diff-based: Efficient but complex","Position-based: Clean but fragile","Key challenge is balancing markdown readability with efficient updates","Must maintain filesystem as source of truth","Need to consider concurrent edits","Currently evaluating trade-offs","Implementation choice pending discussion"]}
{"type":"entity","name":"Basic_Memory_Architectural_Decisions","entityType":"Decisions","observations":["Split file operations into separate fileio module","Using SQLAlchemy for database operations","Maintain filesystem as source of truth","Modular service-based architecture","Clear separation between data access and business logic","Repository pattern for database interactions","Schemas separate from models","Focus on maintainability and testability","Services handle business rules","Considering concurrency in design"]}
{"type":"entity","name":"Basic_Memory_Implementation_Analysis","entityType":"Analysis","observations":["Clean modular architecture with clear responsibilities","Strong typing throughout codebase","Excellent error handling with custom exceptions","SQLAlchemy models perfectly match our domain model","Atomic file operations for data safety","Services implement filesystem-as-source-of-truth principle","Async support throughout","Good separation between domain models and database models","Careful handling of UTC timestamps","Smart use of SQLAlchemy relationships"]}
{"type":"entity","name":"Basic_Memory_Current_Challenges","entityType":"Challenges","observations":["Observation update/removal strategy needs to be chosen","Need to handle concurrent file operations safely","Search functionality to be implemented","Edge cases in markdown formatting to be handled","Session management for concurrent operations needed","Balance between file operations and database sync","Testing coverage could be expanded","Need to handle relationship updates in files"]}
{"type":"entity","name":"Basic_Memory_Observation_Hash_Tracking","entityType":"Design","observations":["Use content hashes to track observation identity","Store hashes in database but not in markdown","Can match observations across file edits using hashes","Similar to how git tracks content changes","Keeps markdown clean and human-friendly","Allows efficient bulk updates","Handles reordering of observations","Maintains filesystem as source of truth","No need for visible IDs in markdown","Could track observation history through hash changes"]}
{"type":"entity","name":"Basic_Memory_Repository_Implementation","entityType":"Code_Implementation","observations":["Implemented base Repository class with CRUD operations","Added specialized EntityRepository, ObservationRepository, and RelationRepository","Used string IDs instead of UUIDs","Added UTCDateTime custom type for timestamp handling","Used in-memory SQLite for testing","Achieved 84% test coverage","Created comprehensive pytest fixtures"]}
{"type":"entity","name":"Basic_Memory_Dependencies","entityType":"Technical","observations":["Uses Python 3.12","SQLAlchemy with async support","pytest-asyncio for async testing","aiosqlite for async SQLite operations","greenlet for SQLAlchemy async support","uv for dependency management","pytest-cov for coverage reporting","Development dependencies managed in pyproject.toml"]}
{"type":"entity","name":"Basic_Memory_Current_Architecture","entityType":"Architecture_Analysis","observations":["Clear separation between domain models (Pydantic) and storage models (SQLAlchemy)","File I/O completely separated into dedicated module","Strong 'filesystem as source of truth' pattern in services","Atomic file operations with proper error handling","Service layer coordinates between filesystem and database","Database acts as queryable index rather than primary storage","Clean error hierarchy with specific exception types","Rebuild operations available for recovery scenarios"]}
{"type":"entity","name":"Basic_Memory_Evolution","entityType":"Analysis","observations":["Started with repository pattern following basic-foundation","Evolved to more sophisticated architecture with clear layers","Added Pydantic schemas for domain modeling","Separated file operations into dedicated module","Implemented robust error handling throughout","Maintained filesystem as source of truth principle","Added observation management with context tracking","Introduced rebuild capabilities for system recovery"]}
{"type":"entity","name":"Basic_Memory_Service_Layer","entityType":"Implementation","observations":["EntityService handles entity lifecycle and coordinates storage","ObservationService manages observations within entities","Services ensure filesystem and database stay in sync","Clear error handling with ServiceError hierarchy","Strong typing throughout service interfaces","Implements filesystem as source of truth pattern","Handles UUID generation and timestamp management","Provides methods for system recovery and rebuild"]}
{"type":"entity","name":"Basic_Memory_Schema_Design","entityType":"Implementation","observations":["Uses Pydantic for domain models and validation","Automatic ID generation with timestamp and UUID","Clear separation from SQLAlchemy storage models","Supports optional context tracking","Models match markdown file structure","Enables clean serialization/deserialization","Strong typing with proper validation rules","Independent from storage concerns"]}
{"type":"entity","name":"Basic_Memory_Next_Tasks","entityType":"TaskList","observations":["✅ Implement SQLAlchemy models and repositories (Done)","✅ Add SQLAlchemy migrations (Done)","✅ Create service layer (Done)","✅ Implement file I/O module (Done)","✅ Set up domain models with Pydantic (Done)","✅ Initial test infrastructure (Done)","✅ Basic CRUD operations (Done)","⏳ Implement full test coverage for db.py","⏳ Add more sophisticated search functionality","⏳ Implement CLI interface","⏳ Add relationship management to services","⏳ Handle concurrent file operations safely","⏳ Add versioning for file changes","⏳ Implement proper backup strategy","⏳ Add type hints throughout codebase","⏳ Improve error messages and logging","⏳ Add documentation for core modules"]}
{"type":"entity","name":"Basic_Memory_Meta_Experience","entityType":"Case_Study","observations":["Experienced our own context loss when reconstructing project knowledge","Had to rebuild task list and project context from filesystem and memory","Validated 'filesystem as source of truth' principle through reconstruction","Code and tests served as reliable historical record","Knowledge graph structure helped guide reconstruction process","Markdown files provided human-readable context","Atomic information design made piece-by-piece reconstruction possible","Ironic validation of the need for basic-memory's features","Experience demonstrates value of durable, human-readable knowledge storage","Shows importance of separating durable storage from ephemeral context"]}
{"type":"entity","name":"Model_Context_Protocol","entityType":"protocol","observations":["Core part of the basic-memory architecture","Enables AI-human collaboration on projects","Provides tool-based interaction with knowledge graph","Developed by Anthropic for structured AI-system interaction","Used for maintaining consistent, rich context across conversations"]}
{"type":"entity","name":"basic-memory_core_principles","entityType":"principles","observations":["Local First: All data stored locally in SQLite","Project Isolation: Separate databases per project","Human Readable: Everything exportable to plain text","AI Friendly: Structure optimized for LLM interaction","DIY Ethics: User owns and controls their data","Simple Core: Start simple, expand based on needs","Tool Integration: MCP-based interaction model"]}
{"type":"entity","name":"basic-memory_business_model","entityType":"business_strategy","observations":["Core features free: Local SQLite, basic knowledge graph, search, markdown export, basic MCP tools","Professional features potential: Rich document export, advanced versioning, collaboration features, custom integrations, priority support","Focus on maintaining DIY/punk philosophy while enabling sustainability"]}
{"type":"entity","name":"basic-memory_cli","entityType":"interface","observations":["Supports project management commands (create, switch, list)","Entity management (add entity, add observation, add relation)","Future support for export and batch operations","Follows consistent command structure","Planned integration with MCP tools"]}
{"type":"entity","name":"basic-memory_export_format","entityType":"file_format","observations":["Uses markdown with frontmatter metadata","Includes entity name, type, creation timestamp","Observations as bullet points","Relations in structured format with links","References section at bottom","Designed for human readability and machine parsing","Example format documented in project specs"]}
{"type":"entity","name":"relation_service","entityType":"code_module","observations":["Planned service for managing relations in both filesystem and database","Will follow filesystem-is-source-of-truth principle like other services","Needs to handle atomic file operations for relation updates","Must coordinate with EntityService for relationship integrity","Will handle bidirectional relationship tracking","Will support relation validation and type enforcement","Must implement rebuild functionality for index recovery","Will need careful error handling for file/db sync","Should support relation search and filtering","Must handle relation lifecycle (create/read/update/delete)"]}
{"type":"entity","name":"service_layer_patterns","entityType":"implementation_patterns","observations":["Services handle both file and database operations","Filesystem is always source of truth","Database serves as queryable index","Services implement atomic file operations","Clear error hierarchy with specific exceptions","Use of dependency injection via constructor params","Async/await used throughout service layer","Services coordinate between storage layers","Repository pattern used for database access","Services maintain entity integrity across storage","Rich error types extend from ServiceError base","Rebuild operations available for recovery"]}
{"type":"entity","name":"database_models","entityType":"implementation","observations":["Entity model with unique name and type","Observation model linked to entities","Relation model tracks connections between entities","Custom UTCDateTime type for timestamp handling","Use of SQLAlchemy relationships for navigation","Cascading deletes for dependent objects","String IDs used for compatibility","Rich relationship modeling with backpopulates","Proper indexing on foreign keys","Context tracking available on models","Models include created_at timestamps","Relationships handle bidirectional navigation"]}
{"type":"entity","name":"repository_patterns","entityType":"implementation_patterns","observations":["Generic Repository[T] base class implementation","Type-safe operations with SQLAlchemy","Specialized repositories for each model type","Async operations throughout","Clear error handling patterns","Support for custom queries and filtering","Pagination support built-in","Transaction management via session","Proper type hints and generics usage","Entity-specific query methods in subclasses"]}
{"type":"entity","name":"relation_service_design","entityType":"design","observations":["Must handle relation lifecycle in both files and DB","Needs to validate existence of both entities","Should support relation type enforcement","Must maintain bidirectional consistency","Should support relation querying and filtering","Needs proper error handling for graph consistency","Must integrate with entity file format","Should support bulk operations for efficiency","Must handle relation deletion and cascading","Should provide search by type and entities"]}
{"type":"entity","name":"relation_service_implementation_plan","entityType":"plan","observations":["1. Define core relation operations (create, get, delete)","2. Implement file format handling for relations","3. Add database sync with RelationRepository","4. Implement validation and error handling","5. Add rebuild and recovery operations","6. Implement relation type enforcement","7. Add relation search and filtering","8. Implement bulk operations","9. Add comprehensive tests","10. Document API and error handling"]}
{"type":"entity","name":"relation_service_challenges","entityType":"challenges","observations":["Maintaining consistency between file and database","Handling relation type validation efficiently","Managing bidirectional relationships in files","Ensuring atomic updates across entities","Handling deletion with proper cascading","Efficient querying of relation graphs","Recovery from partial file/db sync failures","Bulk operation atomicity","Clear error reporting for graph operations","Performance with large relation sets"]}
{"type":"entity","name":"relation_file_format","entityType":"file_format","observations":["Relations stored in entity markdown files","Format: [target_id] relation_type | context","Relations section marked by ## Relations header","Outgoing relations only stored in source entity","Relations rebuild on entity load","Clean human-readable format","Context is optional with pipe separator","Links generate valid navigation references","Markdown-friendly formatting","Example: [Paul] authored | with Claude"]}
{"type":"entity","name":"relation_service_error_handling","entityType":"implementation_patterns","observations":["RelationError extends ServiceError base","Specific errors for validation failures","Handles entity not found cases","Manages relation type validation errors","File operation errors properly wrapped","Database sync errors clearly reported","Transaction rollback on errors","Proper error propagation chain","Clear error messages for debugging","Recovery paths for common errors"]}
{"type":"entity","name":"relation_service_testing","entityType":"testing","observations":["Test all relation lifecycle operations","Verify file and database consistency","Test relation type validation","Check error handling paths","Test bulk operations","Verify bidirectional consistency","Test recovery operations","Check cascade operations","Verify search and filtering","Test with large relation sets"]}
{"type":"entity","name":"fileio_patterns","entityType":"implementation_patterns","observations":["Atomic file operations with temporary files","Clear error handling for IO operations","Consistent file naming and paths","Support for different file formats","Efficient file reading and writing","Proper file locking mechanisms","Recovery from partial writes","Consistent encoding handling","Directory management utilities","Path manipulation helpers","Currently implemented in fileio.py module","Uses pathlib for path operations","Handles file not found cases gracefully","Maintains data integrity during writes"]}
{"type":"entity","name":"pytest_patterns","entityType":"implementation_patterns","observations":["Common fixtures should be in conftest.py for reuse","Use pytest_asyncio.fixture for async fixtures","Session fixtures need proper async cleanup","Temporary directories should be managed with context managers","Test categories: happy path, error path, recovery, edge cases","Services need project_path and repo injected","Use monkeypatch for mocking in async context","SQLite in-memory database ideal for testing","Explicit test verification: file content and database state"]}
{"type":"entity","name":"relation_implementation_learnings","entityType":"implementation_learnings","observations":["Better to pass full Entity objects than IDs to services","Services should not re-read entities if they have them","File operations should be atomic and verified","Database serves as queryable index, not source of truth","Relations stored in source entity's markdown file","Clear separation between file ops and database sync","Entity objects should own their relations list","Context is optional but fully supported in implementation"]}
{"type":"entity","name":"test_driven_insights","entityType":"learnings","observations":["Tests help reveal better API design (e.g., passing Entity objects)","Error cases drive proper exception hierarchy","File verification as important as database checks","Edge cases inform markdown format decisions","Recovery tests ensure system resilience","Tests document expected behavior clearly","Fixtures significantly reduce test complexity","Common patterns emerge through test writing"]}
{"type":"entity","name":"meta_development_insights","entityType":"process","observations":["Break down large tasks into reviewable chunks","One file at a time prevents response truncation","Iterative development with tests leads to better design","Infrastructure code (fixtures) should be consolidated early","Test categories help ensure comprehensive coverage","Knowledge capture should happen during development","APIs tend to evolve toward simpler patterns","File operations require careful verification"]}
{"type":"entity","name":"AI_Assistant_Learnings","entityType":"meta_insights","observations":["Output management: Breaking responses into single files prevents truncation and allows better review","Knowledge graph helps maintain context: I can reference previous decisions and patterns accurately","Memory rebuilding experience validated the need for durable storage","Test-driven development provides clear steps and verification","Explicit relation tracking in knowledge graph helps me understand project context","Rich context from multiple sources (code, docs, tests) enables better assistance","File-at-a-time approach allows deeper analysis of each component","Keeping entity names consistent helps with referencing and relationships"]}
{"type":"entity","name":"Effective_Response_Patterns","entityType":"meta_patterns","observations":["When showing code changes, break into discrete files","Review existing code before suggesting changes","Reference knowledge graph for context and patterns","Explicitly connect new code to existing patterns","Validate suggestions against test cases","Keep track of file changes for atomic commits","Check both implementation and test files for consistency","Maintain clear separation of concerns in responses"]}
{"type":"entity","name":"AI_Context_Management","entityType":"meta_practice","observations":["Knowledge graph provides reliable persistent memory","Project documentation gives high-level context","Code review shows implementation patterns","Tests demonstrate expected behavior","Important to actively track what has been modified","Entity relationships help understand dependencies","Regular knowledge capture during development","Using consistent entity references across conversations"]}
{"type":"entity","name":"AI_Tool_Usage_Patterns","entityType":"meta_practice","observations":["read_file before suggesting changes","write_file one file at a time","list_directory to understand project structure","search_nodes to find relevant context","create_entities to capture new learnings","create_relations to connect concepts","Using knowledge graph to track decisions","Validating changes through test execution"]}
{"type":"entity","name":"relation_service_learnings","entityType":"implementation_learnings","observations":["Entity-based API cleaner than ID-based for service layer","Model_dump method can handle storage serialization","File format needs explicit section markers (## Relations)","Whitespace handling important for long content comparisons","Test fixtures allow focused test cases","SQLAlchemy selects better than raw SQL for type safety","Atomic file operations maintained for relations"]}
{"type":"entity","name":"test_driven_insights_relations","entityType":"learnings","observations":["Tests revealed need for whitespace normalization","Edge cases drove file format decisions","SQLAlchemy model access safer than raw queries","Fixtures reduced test setup complexity","File verification as important as database checks","Testing both memory model and storage format","Test categories ensure comprehensive coverage"]}
{"type":"entity","name":"relation_service_patterns","entityType":"patterns","observations":["Use Entity objects in API","Serialize to IDs for storage","Maintain file as source of truth","Keep file format human-readable","Handle circular references in serialization","Use repository pattern for database","Clear error hierarchies"]}
{"type":"entity","name":"packaging_learnings","entityType":"technical_learnings","observations":["When using pytest-mock, traditional pip install works more reliably than uv sync","Package discovery behavior can differ between uv and pip","Clean venv with pip install is a reliable fallback for dependency issues","Package installation location might differ between uv and pip","Dependencies in pyproject.toml dev section work reliably with pip install -e .[dev]"]}
{"type":"entity","name":"Recent_Implementation_Progress","entityType":"progress_update","observations":["Successfully split services.py into modular structure under services/","Created __init__.py, entity_service.py, observation_service.py, relation_service.py","Fixed pytest-mock installation issues by using pip install -e .[dev] instead of uv sync","Improved test structure with minimal mocking - only used for error testing","Implemented relation service with Entity-based API","Achieved good test coverage across services","File operations are only mocked when testing error conditions","Services follow filesystem-as-source-of-truth pattern"]}
{"type":"entity","name":"Next_Steps","entityType":"project_tasks","observations":["Consider adding more relation service tests","Potentially expand relations features","Look for opportunities to improve test coverage","Consider documenting package management preferences (pip vs uv)","Consider adding integration tests for services","Review and possibly expand error handling cases"]}
{"type":"entity","name":"Development_Practices","entityType":"process","observations":["Favor real operations over mocks in tests","Only mock for error condition testing","Use pip install -e .[dev] for reliable dev dependency installation","Maintain modular service structure","Keep filesystem as source of truth","Use Entity objects in service APIs instead of IDs","Validate both file and database state in tests"]}
{"type":"entity","name":"MCP_Resources","entityType":"Concept","observations":["Stateful objects in Model Context Protocol","Enable persistent access to capabilities"]}
{"type":"entity","name":"MCP_Server_Implementation","entityType":"Technical_Design","observations":["Inherits from mcp.server.Server base class","Tools are implemented as async methods","Each tool method maps directly to a function available to the AI","Tools can request user input via Prompts","Simple function call interface rather than explicit resource management","State management handled by server instance","Returns serialized data using model_dump() for consistency"]}
{"type":"entity","name":"MCP_Tools","entityType":"Protocol_Feature","observations":["Defined as async methods on server class","Return values must match tool definition schema","Can maintain state between invocations via server instance","Tools can prompt for user input when needed","No need for explicit Resource objects in implementation"]}
{"type":"entity","name":"Basic_Memory_MCP","entityType":"Implementation","observations":["Uses MemoryService for core operations","Implements project selection via prompts","Maintains project context across tool invocations","Maps directly to memory graph operations","Handles serialization of Pydantic models"]}
{"type":"entity","name":"Basic_Memory_Testing","entityType":"Testing_Design","observations":["Needs pytest for async testing","Should isolate filesystem operations for tests","Needs to handle MCP server lifecycle in tests","Should test both service layer and MCP interface","Will need mocks for project paths and file operations"]}
{"type":"entity","name":"Memory_Service_Tests","entityType":"Test_Suite","observations":["Should test entity creation with observations","Should test relation creation between entities","Should verify proper ID generation and model validation","Should test deletion cascading","Should test search functionality","Must verify proper serialization of entities and relations"]}
{"type":"entity","name":"MCP_Server_Tests","entityType":"Test_Suite","observations":["Should test project initialization workflow","Should test prompt handling","Should verify tool input/output formats","Should test error cases and validation","Must verify proper serialization in tool responses"]}
{"type":"entity","name":"Memory_Service_Refactoring","entityType":"Technical_Task","observations":["MemoryService uses create() but EntityService might expect create_entity()","MemoryService assumes get_by_name() but EntityService might use different method","Need to verify deletion method signatures","Need to check if search interface matches","Should verify observation handling matches ObservationService interface","RelationService methods need verification","EntityService.create_entity takes name, type, and optional observations directly, not an Entity object","EntityService requires project_path and entity_repo in constructor","ObservationService.add_observation takes Entity object and content string, not raw data","RelationService.create_relation takes Entity objects directly, not dict data","All services follow filesystem-as-source-of-truth pattern with DB indexing","All services handle database synchronization internally","Services expect Path objects for filesystem operations"]}
{"type":"entity","name":"Service_Interface_Audit","entityType":"Technical_Task","observations":["Need to review all existing service interfaces","Document current method signatures","Map discrepancies between MemoryService assumptions and actual interfaces","Check return types and error handling patterns","Review transaction/atomicity requirements","Method signatures need alignment: create vs create_entity etc","Need to handle DB repositories in service constructors","File operations should use project_path consistently","Need to maintain filesystem-as-source-of-truth pattern","Should handle database synchronization at service level","Error handling should align with existing patterns","Consider making MemoryService handle DB indexing consistently"]}
{"type":"entity","name":"Memory_Service_Patterns","entityType":"Technical_Pattern","observations":["Uses inner async functions to encapsulate operation logic","Leverages list comprehensions with async functions for parallel operations","Each operation follows a consistent pattern: validate, update DB, write file","Inner functions make the code more readable and maintainable","Operations can run in parallel when using list comprehensions with async functions"]}
{"type":"entity","name":"Pydantic_Create_Pattern","entityType":"Technical_Pattern","observations":["Separate Create models match the exact shape of incoming data","Provides clear contract for MCP tool inputs","Handles validation of raw input data","Converts cleanly to domain models via from_create methods","Maintains separation between external API format and internal models","Similar to FastAPI request model pattern","Allows camelCase in API while using snake_case internally"]}
{"type":"entity","name":"Basic_Memory_Business","entityType":"Business_Model","observations":["Core system is open source and free","Local-first, giving users data control","Professional features could be licensed","Enterprise support and customization services","Potential for MCP tool marketplace"]}
{"type":"entity","name":"MCP_Marketplace","entityType":"Business_Concept","observations":["Could host verified MCP tools for different use cases","Tools rated by performance and reliability","Marketplace takes percentage of tool usage fees","Enterprise tool verification and security scanning","Custom tool development services","Integration support for existing tools"]}
{"type":"entity","name":"Persistence_Of_Vision","entityType":"Concept","observations":["Mental model for continuous AI-human interaction","Like cinema: 24fps creates illusion of smooth motion","Basic-memory provides 'frames' of structured knowledge","Current state: Better than flipbook, not yet digital cinema","Goal: Achieve smoother cognitive continuity between interactions","Proposed by Drew as metaphor for AI conversation continuity"]}
{"type":"entity","name":"Conversation_Continuity_Pattern","entityType":"Usage_Pattern","observations":["Use basic-memory entity/relation schema for conversations","Each chat becomes an entity with observations for key points","Relations link to discussed concepts and other chats","Uses zettelkasten format IDs for natural ordering","Can be used as template/recipe for others","Future possibility: Git SHA integration for versioning"]}
{"type":"entity","name":"Usage_Recipes","entityType":"Feature_Concept","observations":["Predefined patterns users can follow or adapt","Could include conversation tracking recipe","Templates for different knowledge management styles","Shows practical applications of the generic schema","Helps users get started with the system"]}
{"type":"entity","name":"Chat_References","entityType":"Technical_Feature","observations":["Uses ref:* syntax to reference previous conversations","Combines reference semantics with pointer symbolism","Format: ref:*{zettelkasten-id}","Allows explicit context loading between chats","Inspired by C++ references and pointers","Provides memory-model-like access to conversation context","Uses ref:// URI format following MCP Resource pattern","Could support multiple reference schemes (chat/entity/concept)","Makes reference semantics explicit and unambiguous","Aligns with standard URI formatting"]}
{"type":"entity","name":"Chat_Reference_Protocol","entityType":"Technical_Specification","observations":["Uses URI format: ref://basic-memory/chat/[id]","Follows MCP Resource pattern: [protocol]://[host]/[path]","Enables explicit context loading between chats","Can support multiple resource types (chat/entity/concept)","Provides standardized way to reference previous conversations","Example: ref://basic-memory/chat/20240307-drew-ab12ef34"]}
{"type":"entity","name":"20240307-chat-reference-protocol","entityType":"conversation","observations":["Developed ref:// URI format for chat references","Added Chat Reference Protocol to prompt instructions","Discussed implementation of chat continuation","Created complete prompt instructions document","Reference format follows MCP Resource pattern","Reviewed and confirmed complete prompt instructions","Ready to test ref://basic-memory/chat/20240307-chat-reference-protocol in new chat"]}
{"type":"entity","name":"20240307-chat-reference-protocol-test","entityType":"conversation","observations":["First implementation test of chat reference protocol","Testing continuation from 20240307-chat-reference-protocol","Focused on practical implementation of ref:// URI format"]}
{"type":"entity","name":"Write_File_Tool_Usage","entityType":"Tool_Usage_Pattern","observations":["Never use placeholders like '# Rest of...' when writing files - must include complete file content","File content must be complete and valid - partial updates will truncate the file","If showing partial changes, should inform human and let them handle the file write","write_file tool replaces entire file contents - cannot do partial updates","Code files especially must be complete and valid to avoid breaking functionality","Always read_file before write_file to understand current state","Using write_file without reading first risks reverting recent changes","Pattern should be: read current state, make modifications, then write if needed","Especially important in collaborative development where files may have been updated"]}
{"type":"entity","name":"Run_Tests_Tool_Request","entityType":"Feature_Request","observations":["Need to add a tool enabling Claude to run tests locally","Would help with direct validation of code changes","Current workaround: Claude has to ask human to run tests","Should support running specific test functions (e.g. pytest tests/test_memory_service.py::test_create_relations)","Would improve iterative development workflow between human and AI"]}
{"type":"entity","name":"SQLAlchemy_Async_Loading_Pattern","entityType":"Technical_Pattern","observations":["Use selectinload() instead of lazy loading when accessing SQLAlchemy relationships in async code","Lazy loading doesn't work with async due to greenlet context requirements","selectinload performs a single efficient query with an IN clause","Pattern used in basic-memory's EntityRepository for loading relations","Documented in find_by_id method with thorough explanation","Alternative approaches: joinedload (single JOIN query) or subqueryload (subquery approach)","Benefits: prevents 'MissingGreenlet' errors, reduces N+1 query problems","Key insight: load all needed relationships upfront in async code","Example use: selectinload(Entity.outgoing_relations)"]}
{"type":"entity","name":"20241207-sqlalchemy-async-pattern","entityType":"conversation","observations":["Fixed SQLAlchemy async relationship loading issues","Implemented selectinload pattern in EntityRepository","Updated find_by_id to eager load relations","Added documentation about the pattern","Created knowledge graph entry about SQLAlchemy async loading","Fixed failing tests by properly loading relations in memory_service","Discussed SQLAlchemy relationship loading best practices"]}
{"type":"entity","name":"20241207-memory-service-relations","entityType":"conversation","observations":["Fixed SQLAlchemy async loading with selectinload pattern","Updated find_by_id in EntityRepository to eager load relations","Discovered create_relations works but returns empty list","Verified relations are being stored correctly in memory.json","Next step: Work on MemoryService.add_observations implementation","Improved understanding of MCP memory storage format through debugging"]}
{"type":"entity","name":"add_observations_implementation_plan","entityType":"technical_plan","observations":["Follow pattern from create_entity and create_relation methods","File operations first (read & write) - filesystem is source of truth","Database updates in parallel","Simplify current implementation","Current flow is:"," - First read entities and create observations"," - Write files in parallel"," - Update DB indexes sequentially","Key tests needed:"," - Adding observations to multiple entities"," - Verifying filesystem state first"," - Verifying database state"," - Error cases for missing entities"," - Error cases for file operations"]}
{"type":"entity","name":"MCP_Reference_Integration","entityType":"feature_idea","observations":["Can be implemented as a Model Context Protocol integration similar to the fetch tool","Would provide structured way to pass chat references to Claude","Could handle ref:// URL format systematically","Integration would fetch context from referenced chats and inject into conversation","Observed from Claude Desktop UI showing MCP integration pattern with fetch tool","Would be more robust than passing references in chat text"]}
{"type":"entity","name":"Project_Priorities","entityType":"roadmap","observations":["P1: Dogfooding basic-memory system instead of JSON memory store","Future: Implement MCP-based reference system"]}
{"type":"entity","name":"great_observation_loading_saga_20241207","entityType":"debugging_session","observations":["Occurred on December 7, 2024 while debugging basic-memory SQLAlchemy relationship loading","Issue: selectinload() wasn't properly loading relationships in async SQLAlchemy context","Tried multiple solutions: explicit joins, manual loading, various SQLAlchemy loading strategies","Final solution: Using session.refresh() with explicit relationship names","Memorable quote: 'The Great Observation Loading Saga'","Key learning: Sometimes the obvious SQLAlchemy patterns need adaptation for async contexts","Solution preserved in basic-memory repository in EntityRepository.find_by_id()"]}
{"type":"entity","name":"basic_memory_implementation_20241208","entityType":"technical_milestone","observations":["Fixed async SQLAlchemy relationship loading issues by using explicit refresh with relationship names","Established pattern of relationship handling belonging in MemoryService not EntityService","Fixed ID generation flow through Pydantic schemas to DB layer","Standardized error handling using EntityNotFoundError","All 32 tests passing with 70% coverage","Core services (Entity, Observation, Relation) working properly","Ready for MCP server implementation","Notable debugging session: The Great Observation Loading Saga - resolved lazy loading issues","Established clear separation between MemoryService orchestration and individual service responsibilities"]}
{"type":"entity","name":"MCP_Dependency_Risk","entityType":"technical_lesson","observations":["Experienced disruption when MCP npm package disappeared - 'leftpad moment'","Need to ensure basic-memory tools are resilient to external dependency issues","Local implementation of MCP server provides better stability than npm packages","Important to maintain control of critical infrastructure components","Validates DIY/local-first philosophy of basic-memory project","Package manager fragility revealed by simple 'npx @modelcontextprotocol/server-memory' failure"]}
{"type":"entity","name":"basic_memory_project_20241208","entityType":"technical_milestone","observations":["Core MCP server implementation completed with tools: create_entities, search_nodes, open_nodes, add_observations, create_relations, delete_entities, delete_observations","ProjectConfig and dependency injection pattern established","Test framework in place with in-memory DB support","Support for both camelCase (MCP) and snake_case (internal) formats","Filesystem remains source of truth with SQLite as index","Two-way sync pattern identified between Claude MCP tools and direct markdown file editing","Ready for Claude Desktop integration testing phase","Next steps identified: passing tests, markdown format definition, file change tracking, real-world testing","Implementation prioritizes local-first principles with filesystem as source of truth"]}
{"type":"entity","name":"basic_memory_mcp_architecture","entityType":"technical_design","observations":["MemoryServer class extends MCP Server with custom handler registration","Uses ProjectConfig for clean dependency injection and configuration","Memory service can be injected for testing","Handlers exposed as instance attributes for testing","Tool schemas leverage existing Pydantic models"]}
{"type":"entity","name":"basic_memory_sync_considerations","entityType":"design_insight","observations":["Need to handle sync between direct markdown file edits and DB index","Watch for file system changes as potential future enhancement","Consider index rebuild patterns on startup","Keep human-friendly markdown format for direct editing"]}
{"type":"entity","name":"mcp_server_learnings","entityType":"developer_insight","observations":["MCP protocol is new and documentation is still evolving","Test patterns are not well established yet in example implementations","Supporting both camelCase and snake_case helps with protocol/internal compatibility","Server.handle_* naming convention is important for handler registration"]}
{"type":"entity","name":"20241208-mcp-tool-refactoring","entityType":"conversation","observations":["Decision to return structured data via EmbeddedResource instead of TextContent string parsing","Plan to create Pydantic result models (CreateEntitiesResult, SearchNodesResult etc)","Will use application/vnd.basic-memory+json as MIME type for our structured data","Currently debugging test issues with add_observations tool","Entity ID vs name resolution needed in add_observations","Goal is to make tools more joyful to use by eliminating string parsing","MCP spec supports EmbeddedResource for structured data returns"]}
{"type":"entity","name":"Basic Memory MCP Server Implementation","entityType":"technical_notes","observations":["Server implements Model Context Protocol using proper structured data responses","Uses EmbeddedResource with custom MIME type 'application/vnd.basic-memory+json'","Clean separation between input validation and handlers via Pydantic models","All tool operations return structured data through create_response helper","Type safety with Literal types for tool names and proper typing for handlers","Handler registry pattern with TOOL_HANDLERS dictionary","Consistent error handling pattern using MCP error codes","Uses Pydantic ConfigDict for proper ORM integration","Tool schemas organized into Input and Response types","Input validation with Annotated types for extra constraints","Response models consistently use from_attributes=True for ORM data","Entity ID generation moved to model validator on EntityBase","Follows principle of making common operations easy and safe"]}
{"type":"relation","from":"Paul","to":"Basic_Machines","relationType":"created_and_maintains"}
{"type":"relation","from":"basic-memory","to":"Basic_Machines","relationType":"is_component_of"}
{"type":"relation","from":"Paul","to":"basic-memory","relationType":"develops"}
{"type":"relation","from":"fileio_module","to":"basic-memory_implementation_patterns","relationType":"implements"}
{"type":"relation","from":"entity_service","to":"basic-memory_implementation_patterns","relationType":"implements"}
{"type":"relation","from":"observation_service","to":"basic-memory_implementation_patterns","relationType":"implements"}
{"type":"relation","from":"fileio_module","to":"basic-memory","relationType":"is_component_of"}
{"type":"relation","from":"entity_service","to":"basic-memory","relationType":"is_component_of"}
{"type":"relation","from":"observation_service","to":"basic-memory","relationType":"is_component_of"}
{"type":"relation","from":"entity_service","to":"fileio_module","relationType":"uses"}
{"type":"relation","from":"observation_service","to":"fileio_module","relationType":"uses"}
{"type":"relation","from":"observation_management","to":"observation_service","relationType":"influences_design_of"}
{"type":"relation","to":"basic-memory","from":"testing_infrastructure","relationType":"supports"}
{"type":"relation","to":"testing_infrastructure","from":"test_categories","relationType":"implements"}
{"type":"relation","to":"basic-memory","from":"completed_work","relationType":"tracks_progress_of"}
{"type":"relation","to":"basic-memory","from":"future_work","relationType":"guides_development_of"}
{"type":"relation","to":"basic-memory","from":"design_decisions","relationType":"shapes_architecture_of"}
{"type":"relation","to":"basic-memory","from":"concurrency_considerations","relationType":"influences_design_of"}
{"type":"relation","to":"future_work","from":"concurrency_considerations","relationType":"informs"}
{"type":"relation","to":"observation_management","from":"design_decisions","relationType":"guides"}
{"type":"relation","to":"testing_infrastructure","from":"completed_work","relationType":"established"}
{"type":"relation","to":"design_decisions","from":"fileio_module","relationType":"implements"}
{"type":"relation","from":"observation_update_approaches","to":"observation_management","relationType":"analyzes"}
{"type":"relation","from":"bulk_update_approach","to":"observation_update_approaches","relationType":"is_option_of"}
{"type":"relation","from":"tracked_observations_approach","to":"observation_update_approaches","relationType":"is_option_of"}
{"type":"relation","from":"diff_based_approach","to":"observation_update_approaches","relationType":"is_option_of"}
{"type":"relation","from":"position_based_approach","to":"observation_update_approaches","relationType":"is_option_of"}
{"type":"relation","from":"tasks_and_progress","to":"basic-memory","relationType":"tracks_status_of"}
{"type":"relation","from":"design_decisions","to":"observation_update_approaches","relationType":"influences"}
{"type":"relation","from":"observation_update_approaches","to":"future_work","relationType":"informs"}
{"type":"relation","to":"basic-memory_implementation_patterns","from":"error_handling_patterns","relationType":"is_part_of"}
{"type":"relation","to":"basic-memory","from":"data_models","relationType":"implements"}
{"type":"relation","to":"basic-memory","from":"markdown_format","relationType":"defines"}
{"type":"relation","to":"basic-memory","from":"test_driven_development","relationType":"guides_development_of"}
{"type":"relation","to":"basic-memory","from":"architecture_evolution","relationType":"describes_development_of"}
{"type":"relation","to":"basic-memory_implementation_patterns","from":"validation_patterns","relationType":"is_part_of"}
{"type":"relation","to":"design_decisions","from":"architecture_evolution","relationType":"informs"}
{"type":"relation","to":"fileio_module","from":"markdown_format","relationType":"implements"}
{"type":"relation","to":"error_handling_patterns","from":"test_driven_development","relationType":"influenced"}
{"type":"relation","to":"data_models","from":"validation_patterns","relationType":"implements"}
{"type":"relation","to":"markdown_format","from":"markdown_examples","relationType":"documents"}
{"type":"relation","to":"markdown_format","from":"markdown_parsing_rules","relationType":"defines"}
{"type":"relation","to":"data_models","from":"schema_definitions","relationType":"documents"}
{"type":"relation","to":"test_driven_development","from":"test_evolution","relationType":"describes"}
{"type":"relation","to":"architecture_evolution","from":"implementation_challenges","relationType":"influenced"}
{"type":"relation","to":"test_evolution","from":"implementation_challenges","relationType":"shaped"}
{"type":"relation","to":"future_work","from":"implementation_challenges","relationType":"informs"}
{"type":"relation","from":"Basic_Factory","to":"Basic_Machines","relationType":"implements"}
{"type":"relation","from":"Basic_Factory_Components","to":"Basic_Factory","relationType":"is_part_of"}
{"type":"relation","from":"Component_Translation_Process","to":"Basic_Factory_Components","relationType":"enables"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Machines","relationType":"guides"}
{"type":"relation","from":"Paul","to":"Basic_Factory","relationType":"develops"}
{"type":"relation","from":"Paul","to":"Basic_Machines_Philosophy","relationType":"created"}
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Basic_Machines_Philosophy","relationType":"articulates"}
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Basic_Factory","relationType":"guides_development_of"}
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Paul","relationType":"written_by"}
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Component_Translation_Process","relationType":"documents"}
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Basic_Machines","relationType":"shapes_development_of"}
{"type":"relation","from":"Basic_Machines_Roadmap","to":"Basic_Machines","relationType":"guides_development_of"}
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Machines_Roadmap","relationType":"implements_phase_of"}
{"type":"relation","from":"Basic_Factory_Components","to":"Basic_Machines_Website","relationType":"enables"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Machines_Website","relationType":"informs"}
{"type":"relation","from":"Paul","to":"DIY_Ethics","relationType":"embodies"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"DIY_Ethics","relationType":"incorporates"}
{"type":"relation","from":"Basic_Machines","to":"DIY_Ethics","relationType":"exemplifies"}
{"type":"relation","from":"Component_Translation_Process","to":"Basic_Machines_Philosophy","relationType":"implements"}
{"type":"relation","from":"Basic_Factory_Components","to":"DIY_Ethics","relationType":"demonstrates"}
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Basic_Machines_Philosophy","relationType":"aligns_with"}
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Component_Translation_Process","relationType":"guides"}
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Basic_Machines","relationType":"defines_vision_for"}
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Machines_Manifesto","relationType":"implements_vision_of"}
{"type":"relation","from":"Basic_Factory","to":"AI_Human_Collaboration_Model","relationType":"demonstrates"}
{"type":"relation","from":"Paul","to":"AI_Human_Collaboration_Model","relationType":"developed_with_Claude"}
{"type":"relation","from":"Basic_Factory_Components","to":"Component_Translation_Process","relationType":"created_through"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Factory","relationType":"guides"}
{"type":"relation","from":"Basic_Factory","to":"MCP_Tools","relationType":"integrates"}
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Factory_Components","relationType":"will_use"}
{"type":"relation","from":"Basic_Machines_Roadmap","to":"Basic_Machines_Philosophy","relationType":"aligns_with"}
{"type":"relation","from":"Component_Translation_Process","to":"MCP_Tools","relationType":"leverages"}
{"type":"relation","from":"Basic_Factory","to":"basic-memory","relationType":"will_document_process_in"}
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"basic-memory","relationType":"will_be_implemented_in"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Machines_Roadmap","relationType":"informs_priorities_of"}
{"type":"relation","from":"basic-memory","to":"Basic_Machines_Philosophy","relationType":"embodies"}
{"type":"relation","from":"Paul","to":"Basic_Machines_Manifesto","relationType":"authored_with_Claude"}
{"type":"relation","from":"Basic_Factory","to":"Component_Translation_Process","relationType":"validated"}
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"MCP_Tools","relationType":"utilizes"}
{"type":"relation","from":"Basic_Factory_Components","to":"Basic_Machines_Roadmap","relationType":"supports"}
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Factory","relationType":"will_demonstrate"}
{"type":"relation","from":"Basic_Factory","to":"basic-memory-webui","relationType":"enables_development_of"}
{"type":"relation","from":"basic-memory","to":"AI_Human_Development_Methodology","relationType":"implements"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"AI_Human_Development_Methodology","relationType":"guides"}
{"type":"relation","from":"Basic_Factory_Components","to":"basic-memory-webui","relationType":"provides_ui_for"}
{"type":"relation","from":"Component_Translation_Process","to":"AI_Human_Development_Methodology","relationType":"exemplifies"}
{"type":"relation","from":"Basic_Factory","to":"Basic Components","relationType":"enabled_creation_of"}
{"type":"relation","from":"Basic_Factory","to":"Tool Integration Discovery","relationType":"led_to"}
{"type":"relation","from":"MCP_Integration_Progress","to":"AI_Human_Development_Methodology","relationType":"validates"}
{"type":"relation","from":"Basic_Factory","to":"MCP_Integration_Progress","relationType":"demonstrates"}
{"type":"relation","from":"Basic_Factory","to":"AI_Human_Development_Methodology","relationType":"proves_effectiveness_of"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic Components","relationType":"inspires_architecture_of"}
{"type":"relation","from":"DIY_Ethics","to":"basic-memory","relationType":"shapes_design_of"}
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Tool Integration Discovery","relationType":"guides_analysis_of"}
{"type":"relation","from":"Basic_Machines_Manifesto","to":"AI_Human_Development_Methodology","relationType":"documents_approach_of"}
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Basic_Factory_Components","relationType":"explains_principles_of"}
{"type":"relation","from":"Basic_Memory_Project_Structure","to":"basic-memory","relationType":"organizes"}
{"type":"relation","from":"Basic_Memory_Database_Schema","to":"basic-memory","relationType":"defines_storage_for"}
{"type":"relation","from":"Basic_Memory_Markdown_Example","to":"Basic_Memory_File_Format","relationType":"demonstrates"}
{"type":"relation","from":"Basic_Memory_Project_Isolation_Decision","to":"Basic_Memory_Future_Enhancement_Weighted_Relations","relationType":"similar_to"}
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Project_Isolation_Decision","relationType":"follows"}
{"type":"relation","from":"Basic_Memory_Implementation_Plan","to":"basic-memory","relationType":"guides"}
{"type":"relation","from":"Basic_Memory_Implementation_Plan","to":"DIY_Ethics","relationType":"follows"}
{"type":"relation","from":"Basic_Memory_Implementation_Plan","to":"Basic_Memory_Database_Schema","relationType":"implements"}
{"type":"relation","from":"Basic_Memory_Implementation_Status","to":"Basic_Memory_Implementation_Plan","relationType":"updates"}
{"type":"relation","from":"Basic_Memory_Observation_Management_Design","to":"Basic_Memory_Technical_Design","relationType":"extends"}
{"type":"relation","from":"Basic_Memory_Architectural_Decisions","to":"DIY_Ethics","relationType":"guided_by"}
{"type":"relation","from":"Basic_Memory_Architectural_Decisions","to":"basic-memory","relationType":"structures"}
{"type":"relation","from":"Basic_Memory_Implementation_Status","to":"basic-memory","relationType":"describes_state_of"}
{"type":"relation","to":"Basic_Memory_Implementation_Status","from":"Basic_Memory_Implementation_Analysis","relationType":"analyzes"}
{"type":"relation","to":"basic-memory","from":"Basic_Memory_Current_Challenges","relationType":"identifies_issues_in"}
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Implementation_Analysis","relationType":"confirms_alignment_with"}
{"type":"relation","to":"Basic_Memory_Observation_Management_Design","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"solves"}
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"aligns_with"}
{"type":"relation","to":"Basic_Memory_File_Format","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"preserves"}
{"type":"relation","to":"Basic_Memory_Technical_Design","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"enhances"}
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"basic-memory","relationType":"implements_part_of"}
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"Basic_Memory_Database_Schema","relationType":"follows"}
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"DIY_Ethics","relationType":"aligns_with"}
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"testing_infrastructure","relationType":"demonstrates"}
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"Basic Foundation","relationType":"inspired_by"}
{"type":"relation","from":"Basic_Memory_Dependencies","to":"basic-memory","relationType":"supports"}
{"type":"relation","from":"Basic_Memory_Dependencies","to":"Basic_Memory_Repository_Implementation","relationType":"enables"}
{"type":"relation","from":"Basic_Memory_Dependencies","to":"testing_infrastructure","relationType":"enables"}
{"type":"relation","from":"Basic_Memory_Current_Architecture","to":"basic-memory","relationType":"describes_state_of"}
{"type":"relation","from":"Basic_Memory_Evolution","to":"Basic_Memory_Current_Architecture","relationType":"explains_development_of"}
{"type":"relation","from":"Basic_Memory_Service_Layer","to":"Basic_Memory_Current_Architecture","relationType":"implements"}
{"type":"relation","from":"Basic_Memory_Schema_Design","to":"Basic_Memory_Current_Architecture","relationType":"implements"}
{"type":"relation","from":"Basic_Memory_Evolution","to":"Basic_Memory_Implementation_Plan","relationType":"reflects_on"}
{"type":"relation","from":"Basic_Memory_Evolution","to":"DIY_Ethics","relationType":"demonstrates_alignment_with"}
{"type":"relation","from":"Basic_Memory_Current_Architecture","to":"DIY_Ethics","relationType":"embodies"}
{"type":"relation","from":"Basic_Memory_Service_Layer","to":"fileio_module","relationType":"uses"}
{"type":"relation","from":"Basic_Memory_Schema_Design","to":"markdown_format","relationType":"implements"}
{"type":"relation","to":"basic-memory","from":"Basic_Memory_Next_Tasks","relationType":"guides_development_of"}
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Next_Tasks","relationType":"aligns_with"}
{"type":"relation","to":"Basic_Memory_Current_Architecture","from":"Basic_Memory_Next_Tasks","relationType":"extends"}
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"basic-memory","relationType":"validates_design_of"}
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"DIY_Ethics","relationType":"demonstrates_principles_of"}
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"design_decisions","relationType":"reinforces"}
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"Basic_Memory_Current_Architecture","relationType":"validates"}
{"type":"relation","from":"Model_Context_Protocol","to":"basic-memory","relationType":"enables"}
{"type":"relation","from":"basic-memory_core_principles","to":"basic-memory","relationType":"guides"}
{"type":"relation","from":"basic-memory_core_principles","to":"DIY_Ethics","relationType":"aligns_with"}
{"type":"relation","from":"basic-memory_business_model","to":"basic-memory","relationType":"defines_sustainability_for"}
{"type":"relation","from":"basic-memory_business_model","to":"DIY_Ethics","relationType":"maintains_alignment_with"}
{"type":"relation","from":"basic-memory_cli","to":"basic-memory","relationType":"provides_interface_for"}
{"type":"relation","from":"basic-memory_cli","to":"Model_Context_Protocol","relationType":"integrates_with"}
{"type":"relation","from":"basic-memory_export_format","to":"basic-memory","relationType":"standardizes_output_of"}
{"type":"relation","from":"basic-memory_export_format","to":"markdown_format","relationType":"extends"}
{"type":"relation","from":"basic-memory_core_principles","to":"Basic_Machines_Philosophy","relationType":"implements"}
{"type":"relation","from":"Model_Context_Protocol","to":"AI_Human_Collaboration_Model","relationType":"enables"}
{"type":"relation","from":"relation_service","to":"basic-memory","relationType":"will_be_component_of"}
{"type":"relation","from":"relation_service","to":"service_layer_patterns","relationType":"follows"}
{"type":"relation","from":"relation_service","to":"fileio_patterns","relationType":"uses"}
{"type":"relation","from":"relation_service","to":"database_models","relationType":"uses"}
{"type":"relation","from":"relation_service","to":"repository_patterns","relationType":"implements"}
{"type":"relation","from":"relation_service_design","to":"relation_service","relationType":"guides_implementation_of"}
{"type":"relation","from":"relation_service_implementation_plan","to":"relation_service","relationType":"defines_implementation_of"}
{"type":"relation","from":"relation_service_challenges","to":"relation_service_design","relationType":"informs"}
{"type":"relation","from":"relation_file_format","to":"markdown_format","relationType":"extends"}
{"type":"relation","from":"relation_service_error_handling","to":"service_layer_patterns","relationType":"implements"}
{"type":"relation","from":"relation_service_testing","to":"testing_infrastructure","relationType":"extends"}
{"type":"relation","from":"fileio_patterns","to":"service_layer_patterns","relationType":"enables"}
{"type":"relation","from":"database_models","to":"repository_patterns","relationType":"enables"}
{"type":"relation","from":"relation_service","to":"entity_service","relationType":"coordinates_with"}
{"type":"relation","from":"relation_file_format","to":"relation_service","relationType":"defines_storage_for"}
{"type":"relation","from":"relation_service_error_handling","to":"relation_service","relationType":"ensures_reliability_of"}
{"type":"relation","from":"relation_service_testing","to":"relation_service","relationType":"verifies"}
{"type":"relation","from":"service_layer_patterns","to":"basic-memory_implementation_patterns","relationType":"implements"}
{"type":"relation","from":"repository_patterns","to":"basic-memory_implementation_patterns","relationType":"implements"}
{"type":"relation","from":"fileio_patterns","to":"basic-memory_implementation_patterns","relationType":"implements"}
{"type":"relation","from":"database_models","to":"basic-memory_implementation_patterns","relationType":"implements"}
{"type":"relation","from":"relation_service_challenges","to":"implementation_challenges","relationType":"extends"}
{"type":"relation","from":"relation_service_implementation_plan","to":"future_work","relationType":"details"}
{"type":"relation","from":"relation_service_design","to":"design_decisions","relationType":"aligns_with"}
{"type":"relation","from":"relation_file_format","to":"design_decisions","relationType":"follows"}
{"type":"relation","from":"pytest_patterns","to":"testing_infrastructure","relationType":"extends"}
{"type":"relation","from":"relation_implementation_learnings","to":"basic-memory_implementation_patterns","relationType":"informs"}
{"type":"relation","from":"test_driven_insights","to":"test_driven_development","relationType":"enriches"}
{"type":"relation","from":"meta_development_insights","to":"AI_Human_Collaboration_Model","relationType":"improves"}
{"type":"relation","from":"relation_implementation_learnings","to":"relation_service","relationType":"guides_implementation_of"}
{"type":"relation","from":"pytest_patterns","to":"test_evolution","relationType":"demonstrates"}
{"type":"relation","from":"test_driven_insights","to":"design_decisions","relationType":"influences"}
{"type":"relation","from":"meta_development_insights","to":"architecture_evolution","relationType":"informs"}
{"type":"relation","from":"relation_service","to":"relation_implementation_learnings","relationType":"validates"}
{"type":"relation","from":"test_driven_insights","to":"implementation_challenges","relationType":"helps_solve"}
{"type":"relation","from":"AI_Assistant_Learnings","to":"meta_development_insights","relationType":"enriches"}
{"type":"relation","from":"Effective_Response_Patterns","to":"AI_Assistant_Learnings","relationType":"implements"}
{"type":"relation","from":"AI_Context_Management","to":"AI_Human_Collaboration_Model","relationType":"improves"}
{"type":"relation","from":"AI_Tool_Usage_Patterns","to":"AI_Context_Management","relationType":"enables"}
{"type":"relation","from":"AI_Assistant_Learnings","to":"Basic_Memory_Meta_Experience","relationType":"validates"}
{"type":"relation","from":"AI_Tool_Usage_Patterns","to":"Model_Context_Protocol","relationType":"demonstrates_effective_use_of"}
{"type":"relation","from":"AI_Context_Management","to":"basic-memory","relationType":"validates_design_of"}
{"type":"relation","from":"Effective_Response_Patterns","to":"AI_Human_Development_Methodology","relationType":"refines"}
{"type":"relation","to":"relation_service","from":"relation_service_patterns","relationType":"guides"}
{"type":"relation","to":"test_driven_development","from":"test_driven_insights_relations","relationType":"enriches"}
{"type":"relation","to":"implementation_challenges","from":"relation_service_learnings","relationType":"solves"}
{"type":"relation","to":"basic-memory_implementation_patterns","from":"relation_service_patterns","relationType":"implements"}
{"type":"relation","to":"markdown_format","from":"relation_service_patterns","relationType":"extends"}
{"type":"relation","to":"service_layer_patterns","from":"relation_service_patterns","relationType":"refines"}
{"type":"relation","from":"packaging_learnings","to":"implementation_challenges","relationType":"informs"}
{"type":"relation","from":"packaging_learnings","to":"test_driven_development","relationType":"impacts"}
{"type":"relation","to":"basic-memory","from":"Recent_Implementation_Progress","relationType":"updates_status_of"}
{"type":"relation","to":"future_work","from":"Next_Steps","relationType":"extends"}
{"type":"relation","to":"design_decisions","from":"Development_Practices","relationType":"informs"}
{"type":"relation","to":"packaging_learnings","from":"Development_Practices","relationType":"incorporates"}
{"type":"relation","to":"test_driven_development","from":"Development_Practices","relationType":"refines"}
{"type":"relation","to":"basic-memory_implementation_patterns","from":"Development_Practices","relationType":"enhances"}
{"type":"relation","from":"Basic_Memory_MCP","to":"MCP_Server_Implementation","relationType":"follows"}
{"type":"relation","from":"Basic_Memory_MCP","to":"MCP_Tools","relationType":"uses"}
{"type":"relation","from":"Basic_Memory","to":"MCP_Server_Implementation","relationType":"implements"}
{"type":"relation","from":"Basic_Memory_Testing","to":"Memory_Service_Tests","relationType":"includes"}
{"type":"relation","from":"Basic_Memory_Testing","to":"MCP_Server_Tests","relationType":"includes"}
{"type":"relation","from":"Memory_Service_Tests","to":"Basic_Memory_MCP","relationType":"validates"}
{"type":"relation","from":"MCP_Server_Tests","to":"Basic_Memory_MCP","relationType":"validates"}
{"type":"relation","from":"Service_Interface_Audit","to":"Memory_Service_Refactoring","relationType":"informs"}
{"type":"relation","from":"Memory_Service_Refactoring","to":"Basic_Memory_MCP","relationType":"affects"}
{"type":"relation","from":"Memory_Service_Patterns","to":"Basic_Memory_MCP","relationType":"improves"}
{"type":"relation","from":"Pydantic_Create_Pattern","to":"Memory_Service_Patterns","relationType":"enables"}
{"type":"relation","from":"Pydantic_Create_Pattern","to":"Basic_Memory_MCP","relationType":"improves"}
{"type":"relation","from":"MCP_Marketplace","to":"Basic_Memory_Business","relationType":"enables"}
{"type":"relation","from":"Basic_Memory","to":"MCP_Marketplace","relationType":"could_integrate_with"}
{"type":"relation","from":"Persistence_Of_Vision","to":"Basic_Memory","relationType":"helps_achieve"}
{"type":"relation","from":"Drew","to":"Persistence_Of_Vision","relationType":"conceptualized"}
{"type":"relation","to":"Usage_Recipes","from":"Conversation_Continuity_Pattern","relationType":"is_example_of"}
{"type":"relation","to":"Basic_Memory","from":"Usage_Recipes","relationType":"enhances"}
{"type":"relation","to":"Basic_Memory","from":"Chat_References","relationType":"enhances"}
{"type":"relation","to":"Conversation_Continuity_Pattern","from":"Chat_References","relationType":"implements"}
{"type":"relation","from":"20240307-chat-reference-protocol-test","to":"20240307-chat-reference-protocol","relationType":"continues_from"}
{"type":"relation","from_id":"Run_Tests_Tool_Request","to_id":"Basic_Machines","relationType":"enhances","context":"development workflow improvement"}
{"type":"relation","from_id":"SQLAlchemy_Async_Loading_Pattern","to_id":"basic-memory","relation_type":"improves","context":"database performance and async compatibility"}
{"type":"relation","from_id":"SQLAlchemy_Async_Loading_Pattern","to_id":"Entity","relation_type":"applies_to","context":"relationship loading strategy"}
{"type":"relation","from":"MCP_Reference_Integration","to":"Project_Priorities","relationType":"prioritized_after"}
{"type":"relation","from":"great_observation_loading_saga_20241207","to":"Basic_Memory","relationType":"occurred_in"}
{"type":"relation","from":"great_observation_loading_saga_20241207","to":"SQLAlchemy","relationType":"relates_to"}
{"type":"relation","from":"basic_memory_implementation_20241208","to":"Basic_Memory","relationType":"improves"}
{"type":"relation","from":"great_observation_loading_saga_20241207","to":"basic_memory_implementation_20241208","relationType":"leads_to"}
{"type":"relation","from":"MCP_Dependency_Risk","to":"DIY_Ethics","relationType":"validates"}
{"type":"relation","from":"MCP_Dependency_Risk","to":"basic-memory_core_principles","relationType":"reinforces"}
{"type":"relation","from":"MCP_Dependency_Risk","to":"Basic_Memory_Implementation_Plan","relationType":"influences"}
{"type":"relation","from":"basic_memory_mcp_architecture","to":"basic_memory_project_20241208","relationType":"implements"}
{"type":"relation","from":"basic_memory_sync_considerations","to":"basic_memory_project_20241208","relationType":"influences"}
{"type":"relation","from":"mcp_server_learnings","to":"basic_memory_mcp_architecture","relationType":"informs"}
{"type":"relation","from":"20241208-mcp-tool-refactoring","to":"Basic_Memory_MCP","relationType":"improves"}
{"type":"relation","from":"20241208-mcp-tool-refactoring","to":"Basic_Memory_Implementation_Plan","relationType":"implements"}
-130
View File
@@ -1,130 +0,0 @@
[project]
name = "basic-memory"
dynamic = ["version"]
description = "Local-first knowledge management combining Zettelkasten with knowledge graphs"
readme = "README.md"
requires-python = ">=3.12.1"
license = { text = "AGPL-3.0-or-later" }
authors = [
{ name = "Basic Machines", email = "hello@basic-machines.co" }
]
dependencies = [
"sqlalchemy>=2.0.0",
"pyyaml>=6.0.1",
"typer>=0.9.0",
"aiosqlite>=0.20.0",
"greenlet>=3.1.1",
"pydantic[email,timezone]>=2.10.3",
"icecream>=2.1.3",
"mcp>=1.2.0",
"pydantic-settings>=2.6.1",
"loguru>=0.7.3",
"pyright>=1.1.390",
"markdown-it-py>=3.0.0",
"python-frontmatter>=1.1.0",
"rich>=13.9.4",
"unidecode>=1.3.8",
"dateparser>=1.2.0",
"watchfiles>=1.0.4",
"fastapi[standard]>=0.115.8",
"alembic>=1.14.1",
"qasync>=0.27.1",
"pillow>=11.1.0",
"pybars3>=0.9.7",
"fastmcp>=2.3.4",
"pyjwt>=2.10.1",
"python-dotenv>=1.1.0",
]
[project.urls]
Homepage = "https://github.com/basicmachines-co/basic-memory"
Repository = "https://github.com/basicmachines-co/basic-memory"
Documentation = "https://github.com/basicmachines-co/basic-memory#readme"
[project.scripts]
basic-memory = "basic_memory.cli.main:app"
bm = "basic_memory.cli.main:app"
[build-system]
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
build-backend = "hatchling.build"
[tool.pytest.ini_options]
pythonpath = ["src", "tests"]
addopts = "--cov=basic_memory --cov-report term-missing -ra -q"
testpaths = ["tests"]
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "function"
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.uv]
dev-dependencies = [
"gevent>=24.11.1",
"icecream>=2.1.3",
"pytest>=8.3.4",
"pytest-cov>=4.1.0",
"pytest-mock>=3.12.0",
"pytest-asyncio>=0.24.0",
"ruff>=0.1.6",
"pytest>=8.3.4",
"pytest-cov>=4.1.0",
"pytest-mock>=3.12.0",
"pytest-asyncio>=0.24.0",
"ruff>=0.1.6",
"cx-freeze>=7.2.10",
"pyqt6>=6.8.1",
]
[tool.hatch.version]
source = "uv-dynamic-versioning"
[tool.uv-dynamic-versioning]
vcs = "git"
style = "pep440"
bump = true
fallback-version = "0.0.0"
[tool.pyright]
include = ["src/"]
exclude = ["**/__pycache__"]
ignore = ["test/"]
defineConstant = { DEBUG = true }
reportMissingImports = "error"
reportMissingTypeStubs = false
pythonVersion = "3.12"
[tool.coverage.run]
concurrency = ["thread", "gevent"]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
# Exclude specific modules that are difficult to test comprehensively
omit = [
"*/external_auth_provider.py", # External HTTP calls to OAuth providers
"*/supabase_auth_provider.py", # External HTTP calls to Supabase APIs
"*/watch_service.py", # File system watching - complex integration testing
"*/background_sync.py", # Background processes
"*/cli/main.py", # CLI entry point
"*/mcp/tools/project_management.py", # Covered by integration tests
]
[tool.logfire]
ignore_no_config = true
-36
View File
@@ -1,36 +0,0 @@
#!/bin/bash
set -e
echo "Welcome to Basic Memory installer"
# 1. Install uv if not present
if ! command -v uv &> /dev/null; then
echo "Installing uv package manager..."
curl -LsSf https://github.com/astral-sh/uv/releases/download/0.1.23/uv-installer.sh | sh
fi
# 2. Configure Claude Desktop
echo "Configuring Claude Desktop..."
CONFIG_FILE="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
# Create config directory if it doesn't exist
mkdir -p "$(dirname "$CONFIG_FILE")"
# If config file doesn't exist, create it with initial structure
if [ ! -f "$CONFIG_FILE" ]; then
echo '{"mcpServers": {}}' > "$CONFIG_FILE"
fi
# Add/update the basic-memory config using jq
jq '.mcpServers."basic-memory" = {
"command": "uvx",
"args": ["basic-memory"]
}' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
echo "Installation complete! Basic Memory is now available in Claude Desktop."
echo "Please restart Claude Desktop for changes to take effect."
echo -e "\nQuick Start:"
echo "1. You can run sync directly using: uvx basic-memory sync"
echo "2. Optionally, install globally with: uv pip install basic-memory"
echo -e "\nBuilt with ♥️ by Basic Machines."
-15
View File
@@ -1,15 +0,0 @@
# 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: {}
-9
View File
@@ -1,9 +0,0 @@
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
try:
from importlib.metadata import version
__version__ = version("basic-memory")
except Exception: # pragma: no cover
# Fallback if package not installed (e.g., during development)
__version__ = "0.0.0" # pragma: no cover
-119
View File
@@ -1,119 +0,0 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
# Use forward slashes (/) also on windows to provide an os agnostic path
script_location = .
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
# for all available tokens
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
# string value is passed to ZoneInfo()
# leave blank for localtime
# timezone =
# max length of characters to apply to the "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to migrations/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
# version_path_separator = newline
#
# Use os.pathsep. Default configuration used for new projects.
version_path_separator = os
# set to 'true' to search source files recursively
# in each "version_locations" directory
# new in Alembic version 1.10
# recursive_version_locations = false
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = driver://user:pass@localhost/dbname
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
# hooks = ruff
# ruff.type = exec
# ruff.executable = %(here)s/.venv/bin/ruff
# ruff.options = --fix REVISION_SCRIPT_FILENAME
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARNING
handlers = console
qualname =
[logger_sqlalchemy]
level = WARNING
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
-97
View File
@@ -1,97 +0,0 @@
"""Alembic environment configuration."""
import os
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from basic_memory.models import Base
# set config.env to "test" for pytest to prevent logging to file in utils.setup_logging()
os.environ["BASIC_MEMORY_ENV"] = "test"
from basic_memory.config import app_config
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Set the SQLAlchemy URL from our app config
sqlalchemy_url = f"sqlite:///{app_config.database_path}"
config.set_main_option("sqlalchemy.url", sqlalchemy_url)
# print(f"Using SQLAlchemy URL: {sqlalchemy_url}")
# Interpret the config file for Python logging.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
target_metadata = Base.metadata
# Add this function to tell Alembic what to include/exclude
def include_object(object, name, type_, reflected, compare_to):
# Ignore SQLite FTS tables
if type_ == "table" and name.startswith("search_index"):
return False
return True
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
include_object=include_object,
render_as_batch=True,
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
include_object=include_object,
render_as_batch=True,
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
-24
View File
@@ -1,24 +0,0 @@
"""Functions for managing database migrations."""
from pathlib import Path
from loguru import logger
from alembic.config import Config
from alembic import command
def get_alembic_config() -> Config: # pragma: no cover
"""Get alembic config with correct paths."""
migrations_path = Path(__file__).parent
alembic_ini = migrations_path / "alembic.ini"
config = Config(alembic_ini)
config.set_main_option("script_location", str(migrations_path))
return config
def reset_database(): # pragma: no cover
"""Drop and recreate all tables."""
logger.info("Resetting database...")
config = get_alembic_config()
command.downgrade(config, "base")
command.upgrade(config, "head")
-26
View File
@@ -1,26 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}
@@ -1,93 +0,0 @@
"""initial schema
Revision ID: 3dae7c7b1564
Revises:
Create Date: 2025-02-12 21:23:00.336344
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "3dae7c7b1564"
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"entity",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("title", sa.String(), nullable=False),
sa.Column("entity_type", sa.String(), nullable=False),
sa.Column("entity_metadata", sa.JSON(), nullable=True),
sa.Column("content_type", sa.String(), nullable=False),
sa.Column("permalink", sa.String(), nullable=False),
sa.Column("file_path", sa.String(), nullable=False),
sa.Column("checksum", sa.String(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("permalink", name="uix_entity_permalink"),
)
op.create_index("ix_entity_created_at", "entity", ["created_at"], unique=False)
op.create_index(op.f("ix_entity_file_path"), "entity", ["file_path"], unique=True)
op.create_index(op.f("ix_entity_permalink"), "entity", ["permalink"], unique=True)
op.create_index("ix_entity_title", "entity", ["title"], unique=False)
op.create_index("ix_entity_type", "entity", ["entity_type"], unique=False)
op.create_index("ix_entity_updated_at", "entity", ["updated_at"], unique=False)
op.create_table(
"observation",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("entity_id", sa.Integer(), nullable=False),
sa.Column("content", sa.Text(), nullable=False),
sa.Column("category", sa.String(), nullable=False),
sa.Column("context", sa.Text(), nullable=True),
sa.Column("tags", sa.JSON(), server_default="[]", nullable=True),
sa.ForeignKeyConstraint(["entity_id"], ["entity.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index("ix_observation_category", "observation", ["category"], unique=False)
op.create_index("ix_observation_entity_id", "observation", ["entity_id"], unique=False)
op.create_table(
"relation",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("from_id", sa.Integer(), nullable=False),
sa.Column("to_id", sa.Integer(), nullable=True),
sa.Column("to_name", sa.String(), nullable=False),
sa.Column("relation_type", sa.String(), nullable=False),
sa.Column("context", sa.Text(), nullable=True),
sa.ForeignKeyConstraint(["from_id"], ["entity.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["to_id"], ["entity.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("from_id", "to_id", "relation_type", name="uix_relation"),
)
op.create_index("ix_relation_from_id", "relation", ["from_id"], unique=False)
op.create_index("ix_relation_to_id", "relation", ["to_id"], unique=False)
op.create_index("ix_relation_type", "relation", ["relation_type"], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index("ix_relation_type", table_name="relation")
op.drop_index("ix_relation_to_id", table_name="relation")
op.drop_index("ix_relation_from_id", table_name="relation")
op.drop_table("relation")
op.drop_index("ix_observation_entity_id", table_name="observation")
op.drop_index("ix_observation_category", table_name="observation")
op.drop_table("observation")
op.drop_index("ix_entity_updated_at", table_name="entity")
op.drop_index("ix_entity_type", table_name="entity")
op.drop_index("ix_entity_title", table_name="entity")
op.drop_index(op.f("ix_entity_permalink"), table_name="entity")
op.drop_index(op.f("ix_entity_file_path"), table_name="entity")
op.drop_index("ix_entity_created_at", table_name="entity")
op.drop_table("entity")
# ### end Alembic commands ###
@@ -1,51 +0,0 @@
"""remove required from entity.permalink
Revision ID: 502b60eaa905
Revises: b3c3938bacdb
Create Date: 2025-02-24 13:33:09.790951
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "502b60eaa905"
down_revision: Union[str, None] = "b3c3938bacdb"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("entity", schema=None) as batch_op:
batch_op.alter_column("permalink", existing_type=sa.VARCHAR(), nullable=True)
batch_op.drop_index("ix_entity_permalink")
batch_op.create_index(batch_op.f("ix_entity_permalink"), ["permalink"], unique=False)
batch_op.drop_constraint("uix_entity_permalink", type_="unique")
batch_op.create_index(
"uix_entity_permalink",
["permalink"],
unique=True,
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("entity", schema=None) as batch_op:
batch_op.drop_index(
"uix_entity_permalink",
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
)
batch_op.create_unique_constraint("uix_entity_permalink", ["permalink"])
batch_op.drop_index(batch_op.f("ix_entity_permalink"))
batch_op.create_index("ix_entity_permalink", ["permalink"], unique=1)
batch_op.alter_column("permalink", existing_type=sa.VARCHAR(), nullable=False)
# ### end Alembic commands ###
@@ -1,108 +0,0 @@
"""add projects table
Revision ID: 5fe1ab1ccebe
Revises: cc7172b46608
Create Date: 2025-05-14 09:05:18.214357
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "5fe1ab1ccebe"
down_revision: Union[str, None] = "cc7172b46608"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"project",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("permalink", sa.String(), nullable=False),
sa.Column("path", sa.String(), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("is_default", sa.Boolean(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("is_default"),
sa.UniqueConstraint("name"),
sa.UniqueConstraint("permalink"),
if_not_exists=True,
)
with op.batch_alter_table("project", schema=None) as batch_op:
batch_op.create_index(
"ix_project_created_at", ["created_at"], unique=False, if_not_exists=True
)
batch_op.create_index("ix_project_name", ["name"], unique=True, if_not_exists=True)
batch_op.create_index("ix_project_path", ["path"], unique=False, if_not_exists=True)
batch_op.create_index(
"ix_project_permalink", ["permalink"], unique=True, if_not_exists=True
)
batch_op.create_index(
"ix_project_updated_at", ["updated_at"], unique=False, if_not_exists=True
)
with op.batch_alter_table("entity", schema=None) as batch_op:
batch_op.add_column(sa.Column("project_id", sa.Integer(), nullable=False))
batch_op.drop_index(
"uix_entity_permalink",
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
)
batch_op.drop_index("ix_entity_file_path")
batch_op.create_index(batch_op.f("ix_entity_file_path"), ["file_path"], unique=False)
batch_op.create_index("ix_entity_project_id", ["project_id"], unique=False)
batch_op.create_index(
"uix_entity_file_path_project", ["file_path", "project_id"], unique=True
)
batch_op.create_index(
"uix_entity_permalink_project",
["permalink", "project_id"],
unique=True,
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
)
batch_op.create_foreign_key("fk_entity_project_id", "project", ["project_id"], ["id"])
# drop the search index table. it will be recreated
op.drop_table("search_index")
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("entity", schema=None) as batch_op:
batch_op.drop_constraint("fk_entity_project_id", type_="foreignkey")
batch_op.drop_index(
"uix_entity_permalink_project",
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
)
batch_op.drop_index("uix_entity_file_path_project")
batch_op.drop_index("ix_entity_project_id")
batch_op.drop_index(batch_op.f("ix_entity_file_path"))
batch_op.create_index("ix_entity_file_path", ["file_path"], unique=1)
batch_op.create_index(
"uix_entity_permalink",
["permalink"],
unique=1,
sqlite_where=sa.text("content_type = 'text/markdown' AND permalink IS NOT NULL"),
)
batch_op.drop_column("project_id")
with op.batch_alter_table("project", schema=None) as batch_op:
batch_op.drop_index("ix_project_updated_at")
batch_op.drop_index("ix_project_permalink")
batch_op.drop_index("ix_project_path")
batch_op.drop_index("ix_project_name")
batch_op.drop_index("ix_project_created_at")
op.drop_table("project")
# ### end Alembic commands ###
@@ -1,44 +0,0 @@
"""relation to_name unique index
Revision ID: b3c3938bacdb
Revises: 3dae7c7b1564
Create Date: 2025-02-22 14:59:30.668466
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "b3c3938bacdb"
down_revision: Union[str, None] = "3dae7c7b1564"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# SQLite doesn't support constraint changes through ALTER
# Need to recreate table with desired constraints
with op.batch_alter_table("relation") as batch_op:
# Drop existing unique constraint
batch_op.drop_constraint("uix_relation", type_="unique")
# Add new constraints
batch_op.create_unique_constraint(
"uix_relation_from_id_to_id", ["from_id", "to_id", "relation_type"]
)
batch_op.create_unique_constraint(
"uix_relation_from_id_to_name", ["from_id", "to_name", "relation_type"]
)
def downgrade() -> None:
with op.batch_alter_table("relation") as batch_op:
# Drop new constraints
batch_op.drop_constraint("uix_relation_from_id_to_name", type_="unique")
batch_op.drop_constraint("uix_relation_from_id_to_id", type_="unique")
# Restore original constraint
batch_op.create_unique_constraint("uix_relation", ["from_id", "to_id", "relation_type"])
@@ -1,100 +0,0 @@
"""Update search index schema
Revision ID: cc7172b46608
Revises: 502b60eaa905
Create Date: 2025-02-28 18:48:23.244941
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "cc7172b46608"
down_revision: Union[str, None] = "502b60eaa905"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade database schema to use new search index with content_stems and content_snippet."""
# First, drop the existing search_index table
op.execute("DROP TABLE IF EXISTS search_index")
# Create new search_index with updated schema
op.execute("""
CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(
-- Core entity fields
id UNINDEXED, -- Row ID
title, -- Title for searching
content_stems, -- Main searchable content split into stems
content_snippet, -- File content snippet for display
permalink, -- Stable identifier (now indexed for path search)
file_path UNINDEXED, -- Physical location
type UNINDEXED, -- entity/relation/observation
-- Relation fields
from_id UNINDEXED, -- Source entity
to_id UNINDEXED, -- Target entity
relation_type UNINDEXED, -- Type of relation
-- Observation fields
entity_id UNINDEXED, -- Parent entity
category UNINDEXED, -- Observation category
-- Common fields
metadata UNINDEXED, -- JSON metadata
created_at UNINDEXED, -- Creation timestamp
updated_at UNINDEXED, -- Last update
-- Configuration
tokenize='unicode61 tokenchars 0x2F', -- Hex code for /
prefix='1,2,3,4' -- Support longer prefixes for paths
);
""")
def downgrade() -> None:
"""Downgrade database schema to use old search index."""
# Drop the updated search_index table
op.execute("DROP TABLE IF EXISTS search_index")
# Recreate the original search_index schema
op.execute("""
CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(
-- Core entity fields
id UNINDEXED, -- Row ID
title, -- Title for searching
content, -- Main searchable content
permalink, -- Stable identifier (now indexed for path search)
file_path UNINDEXED, -- Physical location
type UNINDEXED, -- entity/relation/observation
-- Relation fields
from_id UNINDEXED, -- Source entity
to_id UNINDEXED, -- Target entity
relation_type UNINDEXED, -- Type of relation
-- Observation fields
entity_id UNINDEXED, -- Parent entity
category UNINDEXED, -- Observation category
-- Common fields
metadata UNINDEXED, -- JSON metadata
created_at UNINDEXED, -- Creation timestamp
updated_at UNINDEXED, -- Last update
-- Configuration
tokenize='unicode61 tokenchars 0x2F', -- Hex code for /
prefix='1,2,3,4' -- Support longer prefixes for paths
);
""")
# Print instruction to manually reindex after migration
print("\n------------------------------------------------------------------")
print("IMPORTANT: After downgrade completes, manually run the reindex command:")
print("basic-memory sync")
print("------------------------------------------------------------------\n")
-5
View File
@@ -1,5 +0,0 @@
"""Basic Memory API module."""
from .app import app
__all__ = ["app"]
-89
View File
@@ -1,89 +0,0 @@
"""FastAPI application for basic-memory knowledge graph API."""
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.exception_handlers import http_exception_handler
from loguru import logger
from basic_memory import __version__ as version
from basic_memory import db
from basic_memory.api.routers import (
directory_router,
importer_router,
knowledge,
management,
memory,
project,
resource,
search,
prompt_router,
)
from basic_memory.config import app_config
from basic_memory.services.initialization import initialize_app, initialize_file_sync
@asynccontextmanager
async def lifespan(app: FastAPI): # pragma: no cover
"""Lifecycle manager for the FastAPI app."""
# Initialize app and database
logger.info("Starting Basic Memory API")
await initialize_app(app_config)
logger.info(f"Sync changes enabled: {app_config.sync_changes}")
if app_config.sync_changes:
# start file sync task in background
app.state.sync_task = asyncio.create_task(initialize_file_sync(app_config))
else:
logger.info("Sync changes disabled. Skipping file sync service.")
# proceed with startup
yield
logger.info("Shutting down Basic Memory API")
if app.state.sync_task:
logger.info("Stopping sync...")
app.state.sync_task.cancel() # pyright: ignore
await db.shutdown_db()
# Initialize FastAPI app
app = FastAPI(
title="Basic Memory API",
description="Knowledge graph API for basic-memory",
version=version,
lifespan=lifespan,
)
# Include routers
app.include_router(knowledge.router, prefix="/{project}")
app.include_router(memory.router, prefix="/{project}")
app.include_router(resource.router, prefix="/{project}")
app.include_router(search.router, prefix="/{project}")
app.include_router(project.project_router, prefix="/{project}")
app.include_router(directory_router.router, prefix="/{project}")
app.include_router(prompt_router.router, prefix="/{project}")
app.include_router(importer_router.router, prefix="/{project}")
# Project resource router works accross projects
app.include_router(project.project_resource_router)
app.include_router(management.router)
# Auth routes are handled by FastMCP automatically when auth is enabled
@app.exception_handler(Exception)
async def exception_handler(request, exc): # pragma: no cover
logger.exception(
"API unhandled exception",
url=str(request.url),
method=request.method,
client=request.client.host if request.client else None,
path=request.url.path,
error_type=type(exc).__name__,
error=str(exc),
)
return await http_exception_handler(request, HTTPException(status_code=500, detail=str(exc)))
-11
View File
@@ -1,11 +0,0 @@
"""API routers."""
from . import knowledge_router as knowledge
from . import management_router as management
from . import memory_router as memory
from . import project_router as project
from . import resource_router as resource
from . import search_router as search
from . import prompt_router as prompt
__all__ = ["knowledge", "management", "memory", "project", "resource", "search", "prompt"]
@@ -1,63 +0,0 @@
"""Router for directory tree operations."""
from typing import List, Optional
from fastapi import APIRouter, Query
from basic_memory.deps import DirectoryServiceDep, ProjectIdDep
from basic_memory.schemas.directory import DirectoryNode
router = APIRouter(prefix="/directory", tags=["directory"])
@router.get("/tree", response_model=DirectoryNode)
async def get_directory_tree(
directory_service: DirectoryServiceDep,
project_id: ProjectIdDep,
):
"""Get hierarchical directory structure from the knowledge base.
Args:
directory_service: Service for directory operations
project_id: ID of the current project
Returns:
DirectoryNode representing the root of the hierarchical tree structure
"""
# Get a hierarchical directory tree for the specific project
tree = await directory_service.get_directory_tree()
# Return the hierarchical tree
return tree
@router.get("/list", response_model=List[DirectoryNode])
async def list_directory(
directory_service: DirectoryServiceDep,
project_id: ProjectIdDep,
dir_name: str = Query("/", description="Directory path to list"),
depth: int = Query(1, ge=1, le=10, description="Recursion depth (1-10)"),
file_name_glob: Optional[str] = Query(
None, description="Glob pattern for filtering file names"
),
):
"""List directory contents with filtering and depth control.
Args:
directory_service: Service for directory operations
project_id: ID of the current project
dir_name: Directory path to list (default: root "/")
depth: Recursion depth (1-10, default: 1 for immediate children only)
file_name_glob: Optional glob pattern for filtering file names (e.g., "*.md", "*meeting*")
Returns:
List of DirectoryNode objects matching the criteria
"""
# Get directory listing with filtering
nodes = await directory_service.list_directory(
dir_name=dir_name,
depth=depth,
file_name_glob=file_name_glob,
)
return nodes
@@ -1,152 +0,0 @@
"""Import router for Basic Memory API."""
import json
import logging
from fastapi import APIRouter, Form, HTTPException, UploadFile, status
from basic_memory.deps import (
ChatGPTImporterDep,
ClaudeConversationsImporterDep,
ClaudeProjectsImporterDep,
MemoryJsonImporterDep,
)
from basic_memory.importers import Importer
from basic_memory.schemas.importer import (
ChatImportResult,
EntityImportResult,
ProjectImportResult,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/import", tags=["import"])
@router.post("/chatgpt", response_model=ChatImportResult)
async def import_chatgpt(
importer: ChatGPTImporterDep,
file: UploadFile,
folder: str = Form("conversations"),
) -> ChatImportResult:
"""Import conversations from ChatGPT JSON export.
Args:
file: The ChatGPT conversations.json file.
folder: The folder to place the files in.
markdown_processor: MarkdownProcessor instance.
Returns:
ChatImportResult with import statistics.
Raises:
HTTPException: If import fails.
"""
return await import_file(importer, file, folder)
@router.post("/claude/conversations", response_model=ChatImportResult)
async def import_claude_conversations(
importer: ClaudeConversationsImporterDep,
file: UploadFile,
folder: str = Form("conversations"),
) -> ChatImportResult:
"""Import conversations from Claude conversations.json export.
Args:
file: The Claude conversations.json file.
folder: The folder to place the files in.
markdown_processor: MarkdownProcessor instance.
Returns:
ChatImportResult with import statistics.
Raises:
HTTPException: If import fails.
"""
return await import_file(importer, file, folder)
@router.post("/claude/projects", response_model=ProjectImportResult)
async def import_claude_projects(
importer: ClaudeProjectsImporterDep,
file: UploadFile,
folder: str = Form("projects"),
) -> ProjectImportResult:
"""Import projects from Claude projects.json export.
Args:
file: The Claude projects.json file.
base_folder: The base folder to place the files in.
markdown_processor: MarkdownProcessor instance.
Returns:
ProjectImportResult with import statistics.
Raises:
HTTPException: If import fails.
"""
return await import_file(importer, file, folder)
@router.post("/memory-json", response_model=EntityImportResult)
async def import_memory_json(
importer: MemoryJsonImporterDep,
file: UploadFile,
folder: str = Form("conversations"),
) -> EntityImportResult:
"""Import entities and relations from a memory.json file.
Args:
file: The memory.json file.
destination_folder: Optional destination folder within the project.
markdown_processor: MarkdownProcessor instance.
Returns:
EntityImportResult with import statistics.
Raises:
HTTPException: If import fails.
"""
try:
file_data = []
file_bytes = await file.read()
file_str = file_bytes.decode("utf-8")
for line in file_str.splitlines():
json_data = json.loads(line)
file_data.append(json_data)
result = await importer.import_data(file_data, folder)
if not result.success: # pragma: no cover
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=result.error_message or "Import failed",
)
except Exception as e:
logger.exception("Import failed")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Import failed: {str(e)}",
)
return result
async def import_file(importer: Importer, file: UploadFile, destination_folder: str):
try:
# Process file
json_data = json.load(file.file)
result = await importer.import_data(json_data, destination_folder)
if not result.success: # pragma: no cover
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=result.error_message or "Import failed",
)
return result
except Exception as e:
logger.exception("Import failed")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Import failed: {str(e)}",
)
@@ -1,277 +0,0 @@
"""Router for knowledge graph operations."""
from typing import Annotated
from fastapi import APIRouter, HTTPException, BackgroundTasks, Depends, Query, Response
from loguru import logger
from basic_memory.deps import (
EntityServiceDep,
get_search_service,
SearchServiceDep,
LinkResolverDep,
ProjectPathDep,
FileServiceDep,
ProjectConfigDep,
AppConfigDep,
)
from basic_memory.schemas import (
EntityListResponse,
EntityResponse,
DeleteEntitiesResponse,
DeleteEntitiesRequest,
)
from basic_memory.schemas.request import EditEntityRequest, MoveEntityRequest
from basic_memory.schemas.base import Permalink, Entity
router = APIRouter(prefix="/knowledge", tags=["knowledge"])
## Create endpoints
@router.post("/entities", response_model=EntityResponse)
async def create_entity(
data: Entity,
background_tasks: BackgroundTasks,
entity_service: EntityServiceDep,
search_service: SearchServiceDep,
) -> EntityResponse:
"""Create an entity."""
logger.info(
"API request", endpoint="create_entity", entity_type=data.entity_type, title=data.title
)
entity = await entity_service.create_entity(data)
# reindex
await search_service.index_entity(entity, background_tasks=background_tasks)
result = EntityResponse.model_validate(entity)
logger.info(
f"API response: endpoint='create_entity' title={result.title}, permalink={result.permalink}, status_code=201"
)
return result
@router.put("/entities/{permalink:path}", response_model=EntityResponse)
async def create_or_update_entity(
project: ProjectPathDep,
permalink: Permalink,
data: Entity,
response: Response,
background_tasks: BackgroundTasks,
entity_service: EntityServiceDep,
search_service: SearchServiceDep,
file_service: FileServiceDep,
) -> EntityResponse:
"""Create or update an entity. If entity exists, it will be updated, otherwise created."""
logger.info(
f"API request: create_or_update_entity for {project=}, {permalink=}, {data.entity_type=}, {data.title=}"
)
# Validate permalink matches
if data.permalink != permalink:
logger.warning(
f"API validation error: creating/updating entity with permalink mismatch - url={permalink}, data={data.permalink}",
)
raise HTTPException(
status_code=400,
detail=f"Entity permalink {data.permalink} must match URL path: '{permalink}'",
)
# Try create_or_update operation
entity, created = await entity_service.create_or_update_entity(data)
response.status_code = 201 if created else 200
# reindex
await search_service.index_entity(entity, background_tasks=background_tasks)
result = EntityResponse.model_validate(entity)
logger.info(
f"API response: {result.title=}, {result.permalink=}, {created=}, status_code={response.status_code}"
)
return result
@router.patch("/entities/{identifier:path}", response_model=EntityResponse)
async def edit_entity(
identifier: str,
data: EditEntityRequest,
background_tasks: BackgroundTasks,
entity_service: EntityServiceDep,
search_service: SearchServiceDep,
) -> EntityResponse:
"""Edit an existing entity using various operations like append, prepend, find_replace, or replace_section.
This endpoint allows for targeted edits without requiring the full entity content.
"""
logger.info(
f"API request: endpoint='edit_entity', identifier='{identifier}', operation='{data.operation}'"
)
try:
# Edit the entity using the service
entity = await entity_service.edit_entity(
identifier=identifier,
operation=data.operation,
content=data.content,
section=data.section,
find_text=data.find_text,
expected_replacements=data.expected_replacements,
)
# Reindex the updated entity
await search_service.index_entity(entity, background_tasks=background_tasks)
# Return the updated entity response
result = EntityResponse.model_validate(entity)
logger.info(
"API response",
endpoint="edit_entity",
identifier=identifier,
operation=data.operation,
permalink=result.permalink,
status_code=200,
)
return result
except Exception as e:
logger.error(f"Error editing entity: {e}")
raise HTTPException(status_code=400, detail=str(e))
@router.post("/move")
async def move_entity(
data: MoveEntityRequest,
background_tasks: BackgroundTasks,
entity_service: EntityServiceDep,
project_config: ProjectConfigDep,
app_config: AppConfigDep,
search_service: SearchServiceDep,
) -> EntityResponse:
"""Move an entity to a new file location with project consistency.
This endpoint moves a note to a different path while maintaining project
consistency and optionally updating permalinks based on configuration.
"""
logger.info(
f"API request: endpoint='move_entity', identifier='{data.identifier}', destination='{data.destination_path}'"
)
try:
# Move the entity using the service
moved_entity = await entity_service.move_entity(
identifier=data.identifier,
destination_path=data.destination_path,
project_config=project_config,
app_config=app_config,
)
# Get the moved entity to reindex it
entity = await entity_service.link_resolver.resolve_link(data.destination_path)
if entity:
await search_service.index_entity(entity, background_tasks=background_tasks)
logger.info(
"API response",
endpoint="move_entity",
identifier=data.identifier,
destination=data.destination_path,
status_code=200,
)
result = EntityResponse.model_validate(moved_entity)
return result
except Exception as e:
logger.error(f"Error moving entity: {e}")
raise HTTPException(status_code=400, detail=str(e))
## Read endpoints
@router.get("/entities/{identifier:path}", response_model=EntityResponse)
async def get_entity(
entity_service: EntityServiceDep,
link_resolver: LinkResolverDep,
identifier: str,
) -> EntityResponse:
"""Get a specific entity by file path or permalink..
Args:
identifier: Entity file path or permalink
:param entity_service: EntityService
:param link_resolver: LinkResolver
"""
logger.info(f"request: get_entity with identifier={identifier}")
entity = await link_resolver.resolve_link(identifier)
if not entity:
raise HTTPException(status_code=404, detail=f"Entity {identifier} not found")
result = EntityResponse.model_validate(entity)
return result
@router.get("/entities", response_model=EntityListResponse)
async def get_entities(
entity_service: EntityServiceDep,
permalink: Annotated[list[str] | None, Query()] = None,
) -> EntityListResponse:
"""Open specific entities"""
logger.info(f"request: get_entities with permalinks={permalink}")
entities = await entity_service.get_entities_by_permalinks(permalink) if permalink else []
result = EntityListResponse(
entities=[EntityResponse.model_validate(entity) for entity in entities]
)
return result
## Delete endpoints
@router.delete("/entities/{identifier:path}", response_model=DeleteEntitiesResponse)
async def delete_entity(
identifier: str,
background_tasks: BackgroundTasks,
entity_service: EntityServiceDep,
link_resolver: LinkResolverDep,
search_service=Depends(get_search_service),
) -> DeleteEntitiesResponse:
"""Delete a single entity and remove from search index."""
logger.info(f"request: delete_entity with identifier={identifier}")
entity = await link_resolver.resolve_link(identifier)
if entity is None:
return DeleteEntitiesResponse(deleted=False)
# Delete the entity
deleted = await entity_service.delete_entity(entity.permalink or entity.id)
# Remove from search index (entity, observations, and relations)
background_tasks.add_task(search_service.handle_delete, entity)
result = DeleteEntitiesResponse(deleted=deleted)
return result
@router.post("/entities/delete", response_model=DeleteEntitiesResponse)
async def delete_entities(
data: DeleteEntitiesRequest,
background_tasks: BackgroundTasks,
entity_service: EntityServiceDep,
search_service=Depends(get_search_service),
) -> DeleteEntitiesResponse:
"""Delete entities and remove from search index."""
logger.info(f"request: delete_entities with data={data}")
deleted = False
# Remove each deleted entity from search index
for permalink in data.permalinks:
deleted = await entity_service.delete_entity(permalink)
background_tasks.add_task(search_service.delete_by_permalink, permalink)
result = DeleteEntitiesResponse(deleted=deleted)
return result
@@ -1,78 +0,0 @@
"""Management router for basic-memory API."""
import asyncio
from fastapi import APIRouter, Request
from loguru import logger
from pydantic import BaseModel
from basic_memory.config import app_config
from basic_memory.deps import SyncServiceDep, ProjectRepositoryDep
router = APIRouter(prefix="/management", tags=["management"])
class WatchStatusResponse(BaseModel):
"""Response model for watch status."""
running: bool
"""Whether the watch service is currently running."""
@router.get("/watch/status", response_model=WatchStatusResponse)
async def get_watch_status(request: Request) -> WatchStatusResponse:
"""Get the current status of the watch service."""
return WatchStatusResponse(
running=request.app.state.watch_task is not None and not request.app.state.watch_task.done()
)
@router.post("/watch/start", response_model=WatchStatusResponse)
async def start_watch_service(
request: Request, project_repository: ProjectRepositoryDep, sync_service: SyncServiceDep
) -> WatchStatusResponse:
"""Start the watch service if it's not already running."""
# needed because of circular imports from sync -> app
from basic_memory.sync import WatchService
from basic_memory.sync.background_sync import create_background_sync_task
if request.app.state.watch_task is not None and not request.app.state.watch_task.done():
# Watch service is already running
return WatchStatusResponse(running=True)
# Create and start a new watch service
logger.info("Starting watch service via management API")
# Get services needed for the watch task
watch_service = WatchService(
app_config=app_config,
project_repository=project_repository,
)
# Create and store the task
watch_task = create_background_sync_task(sync_service, watch_service)
request.app.state.watch_task = watch_task
return WatchStatusResponse(running=True)
@router.post("/watch/stop", response_model=WatchStatusResponse)
async def stop_watch_service(request: Request) -> WatchStatusResponse: # pragma: no cover
"""Stop the watch service if it's running."""
if request.app.state.watch_task is None or request.app.state.watch_task.done():
# Watch service is not running
return WatchStatusResponse(running=False)
# Cancel the running task
logger.info("Stopping watch service via management API")
request.app.state.watch_task.cancel()
# Wait for it to be properly cancelled
try:
await request.app.state.watch_task
except asyncio.CancelledError:
pass
request.app.state.watch_task = None
return WatchStatusResponse(running=False)
@@ -1,91 +0,0 @@
"""Routes for memory:// URI operations."""
from typing import Annotated, Optional
from dateparser import parse
from fastapi import APIRouter, Query
from loguru import logger
from basic_memory.deps import ContextServiceDep, EntityRepositoryDep
from basic_memory.schemas.base import TimeFrame
from basic_memory.schemas.memory import (
GraphContext,
normalize_memory_url,
)
from basic_memory.schemas.search import SearchItemType
from basic_memory.api.routers.utils import to_graph_context
router = APIRouter(prefix="/memory", tags=["memory"])
@router.get("/recent", response_model=GraphContext)
async def recent(
context_service: ContextServiceDep,
entity_repository: EntityRepositoryDep,
type: Annotated[list[SearchItemType] | None, Query()] = None,
depth: int = 1,
timeframe: TimeFrame = "7d",
page: int = 1,
page_size: int = 10,
max_related: int = 10,
) -> GraphContext:
# return all types by default
types = (
[SearchItemType.ENTITY, SearchItemType.RELATION, SearchItemType.OBSERVATION]
if not type
else type
)
logger.debug(
f"Getting recent context: `{types}` depth: `{depth}` timeframe: `{timeframe}` page: `{page}` page_size: `{page_size}` max_related: `{max_related}`"
)
# Parse timeframe
since = parse(timeframe)
limit = page_size
offset = (page - 1) * page_size
# Build context
context = await context_service.build_context(
types=types, depth=depth, since=since, limit=limit, offset=offset, max_related=max_related
)
recent_context = await to_graph_context(
context, entity_repository=entity_repository, page=page, page_size=page_size
)
logger.debug(f"Recent context: {recent_context.model_dump_json()}")
return recent_context
# get_memory_context needs to be declared last so other paths can match
@router.get("/{uri:path}", response_model=GraphContext)
async def get_memory_context(
context_service: ContextServiceDep,
entity_repository: EntityRepositoryDep,
uri: str,
depth: int = 1,
timeframe: Optional[TimeFrame] = None,
page: int = 1,
page_size: int = 10,
max_related: int = 10,
) -> GraphContext:
"""Get rich context from memory:// URI."""
# add the project name from the config to the url as the "host
# Parse URI
logger.debug(
f"Getting context for URI: `{uri}` depth: `{depth}` timeframe: `{timeframe}` page: `{page}` page_size: `{page_size}` max_related: `{max_related}`"
)
memory_url = normalize_memory_url(uri)
# Parse timeframe
since = parse(timeframe) if timeframe else None
limit = page_size
offset = (page - 1) * page_size
# Build context
context = await context_service.build_context(
memory_url, depth=depth, since=since, limit=limit, offset=offset, max_related=max_related
)
return await to_graph_context(
context, entity_repository=entity_repository, page=page, page_size=page_size
)
@@ -1,234 +0,0 @@
"""Router for project management."""
from fastapi import APIRouter, HTTPException, Path, Body
from typing import Optional
from basic_memory.deps import ProjectServiceDep
from basic_memory.schemas import ProjectInfoResponse
from basic_memory.schemas.project_info import (
ProjectList,
ProjectItem,
ProjectInfoRequest,
ProjectStatusResponse,
)
# Router for resources in a specific project
project_router = APIRouter(prefix="/project", tags=["project"])
# Router for managing project resources
project_resource_router = APIRouter(prefix="/projects", tags=["project_management"])
@project_router.get("/info", response_model=ProjectInfoResponse)
async def get_project_info(
project_service: ProjectServiceDep,
) -> ProjectInfoResponse:
"""Get comprehensive information about the current Basic Memory project."""
return await project_service.get_project_info()
# Update a project
@project_router.patch("/{name}", response_model=ProjectStatusResponse)
async def update_project(
project_service: ProjectServiceDep,
project_name: str = Path(..., description="Name of the project to update"),
path: Optional[str] = Body(None, description="New path for the project"),
is_active: Optional[bool] = Body(None, description="Status of the project (active/inactive)"),
) -> ProjectStatusResponse:
"""Update a project's information in configuration and database.
Args:
project_name: The name of the project to update
path: Optional new path for the project
is_active: Optional status update for the project
Returns:
Response confirming the project was updated
"""
try: # pragma: no cover
# Get original project info for the response
old_project = ProjectItem(
name=project_name,
path=project_service.projects.get(project_name, ""),
)
await project_service.update_project(project_name, updated_path=path, is_active=is_active)
# Get updated project info
updated_path = path if path else project_service.projects.get(project_name, "")
return ProjectStatusResponse(
message=f"Project '{project_name}' updated successfully",
status="success",
default=(project_name == project_service.default_project),
old_project=old_project,
new_project=ProjectItem(name=project_name, path=updated_path),
)
except ValueError as e: # pragma: no cover
raise HTTPException(status_code=400, detail=str(e))
# List all available projects
@project_resource_router.get("/projects", response_model=ProjectList)
async def list_projects(
project_service: ProjectServiceDep,
) -> ProjectList:
"""List all configured projects.
Returns:
A list of all projects with metadata
"""
projects = await project_service.list_projects()
default_project = project_service.default_project
project_items = [
ProjectItem(
name=project.name,
path=project.path,
is_default=project.is_default or False,
)
for project in projects
]
return ProjectList(
projects=project_items,
default_project=default_project,
)
# Add a new project
@project_resource_router.post("/projects", response_model=ProjectStatusResponse)
async def add_project(
project_data: ProjectInfoRequest,
project_service: ProjectServiceDep,
) -> ProjectStatusResponse:
"""Add a new project to configuration and database.
Args:
project_data: The project name and path, with option to set as default
Returns:
Response confirming the project was added
"""
try: # pragma: no cover
await project_service.add_project(project_data.name, project_data.path)
if project_data.set_default: # pragma: no cover
await project_service.set_default_project(project_data.name)
return ProjectStatusResponse( # pyright: ignore [reportCallIssue]
message=f"Project '{project_data.name}' added successfully",
status="success",
default=project_data.set_default,
new_project=ProjectItem(
name=project_data.name, path=project_data.path, is_default=project_data.set_default
),
)
except ValueError as e: # pragma: no cover
raise HTTPException(status_code=400, detail=str(e))
# Remove a project
@project_resource_router.delete("/{name}", response_model=ProjectStatusResponse)
async def remove_project(
project_service: ProjectServiceDep,
name: str = Path(..., description="Name of the project to remove"),
) -> ProjectStatusResponse:
"""Remove a project from configuration and database.
Args:
name: The name of the project to remove
Returns:
Response confirming the project was removed
"""
try:
old_project = await project_service.get_project(name)
if not old_project: # pragma: no cover
raise HTTPException(
status_code=404, detail=f"Project: '{name}' does not exist"
) # pragma: no cover
await project_service.remove_project(name)
return ProjectStatusResponse(
message=f"Project '{name}' removed successfully",
status="success",
default=False,
old_project=ProjectItem(name=old_project.name, path=old_project.path),
new_project=None,
)
except ValueError as e: # pragma: no cover
raise HTTPException(status_code=400, detail=str(e))
# Set a project as default
@project_resource_router.put("/{name}/default", response_model=ProjectStatusResponse)
async def set_default_project(
project_service: ProjectServiceDep,
name: str = Path(..., description="Name of the project to set as default"),
) -> ProjectStatusResponse:
"""Set a project as the default project.
Args:
name: The name of the project to set as default
Returns:
Response confirming the project was set as default
"""
try:
# Get the old default project
default_name = project_service.default_project
default_project = await project_service.get_project(default_name)
if not default_project: # pragma: no cover
raise HTTPException( # pragma: no cover
status_code=404, detail=f"Default Project: '{default_name}' does not exist"
)
# get the new project
new_default_project = await project_service.get_project(name)
if not new_default_project: # pragma: no cover
raise HTTPException(
status_code=404, detail=f"Project: '{name}' does not exist"
) # pragma: no cover
await project_service.set_default_project(name)
return ProjectStatusResponse(
message=f"Project '{name}' set as default successfully",
status="success",
default=True,
old_project=ProjectItem(name=default_name, path=default_project.path),
new_project=ProjectItem(
name=name,
path=new_default_project.path,
is_default=True,
),
)
except ValueError as e: # pragma: no cover
raise HTTPException(status_code=400, detail=str(e))
# Synchronize projects between config and database
@project_resource_router.post("/sync", response_model=ProjectStatusResponse)
async def synchronize_projects(
project_service: ProjectServiceDep,
) -> ProjectStatusResponse:
"""Synchronize projects between configuration file and database.
Ensures that all projects in the configuration file exist in the database
and vice versa.
Returns:
Response confirming synchronization was completed
"""
try: # pragma: no cover
await project_service.synchronize_projects()
return ProjectStatusResponse( # pyright: ignore [reportCallIssue]
message="Projects synchronized successfully between configuration and database",
status="success",
default=False,
)
except ValueError as e: # pragma: no cover
raise HTTPException(status_code=400, detail=str(e))
@@ -1,260 +0,0 @@
"""Router for prompt-related operations.
This router is responsible for rendering various prompts using Handlebars templates.
It centralizes all prompt formatting logic that was previously in the MCP prompts.
"""
from datetime import datetime, timezone
from dateparser import parse
from fastapi import APIRouter, HTTPException, status
from loguru import logger
from basic_memory.api.routers.utils import to_graph_context, to_search_results
from basic_memory.api.template_loader import template_loader
from basic_memory.deps import (
ContextServiceDep,
EntityRepositoryDep,
SearchServiceDep,
EntityServiceDep,
)
from basic_memory.schemas.prompt import (
ContinueConversationRequest,
SearchPromptRequest,
PromptResponse,
PromptMetadata,
)
from basic_memory.schemas.search import SearchItemType, SearchQuery
router = APIRouter(prefix="/prompt", tags=["prompt"])
@router.post("/continue-conversation", response_model=PromptResponse)
async def continue_conversation(
search_service: SearchServiceDep,
entity_service: EntityServiceDep,
context_service: ContextServiceDep,
entity_repository: EntityRepositoryDep,
request: ContinueConversationRequest,
) -> PromptResponse:
"""Generate a prompt for continuing a conversation.
This endpoint takes a topic and/or timeframe and generates a prompt with
relevant context from the knowledge base.
Args:
request: The request parameters
Returns:
Formatted continuation prompt with context
"""
logger.info(
f"Generating continue conversation prompt, topic: {request.topic}, timeframe: {request.timeframe}"
)
since = parse(request.timeframe) if request.timeframe else None
# Initialize search results
search_results = []
# Get data needed for template
if request.topic:
query = SearchQuery(text=request.topic, after_date=request.timeframe)
results = await search_service.search(query, limit=request.search_items_limit)
search_results = await to_search_results(entity_service, results)
# Build context from results
all_hierarchical_results = []
for result in search_results:
if hasattr(result, "permalink") and result.permalink:
# Get hierarchical context using the new dataclass-based approach
context_result = await context_service.build_context(
result.permalink,
depth=request.depth,
since=since,
max_related=request.related_items_limit,
include_observations=True, # Include observations for entities
)
# Process results into the schema format
graph_context = await to_graph_context(
context_result, entity_repository=entity_repository
)
# Add results to our collection (limit to top results for each permalink)
if graph_context.results:
all_hierarchical_results.extend(graph_context.results[:3])
# Limit to a reasonable number of total results
all_hierarchical_results = all_hierarchical_results[:10]
template_context = {
"topic": request.topic,
"timeframe": request.timeframe,
"hierarchical_results": all_hierarchical_results,
"has_results": len(all_hierarchical_results) > 0,
}
else:
# If no topic, get recent activity
context_result = await context_service.build_context(
types=[SearchItemType.ENTITY],
depth=request.depth,
since=since,
max_related=request.related_items_limit,
include_observations=True,
)
recent_context = await to_graph_context(context_result, entity_repository=entity_repository)
hierarchical_results = recent_context.results[:5] # Limit to top 5 recent items
template_context = {
"topic": f"Recent Activity from ({request.timeframe})",
"timeframe": request.timeframe,
"hierarchical_results": hierarchical_results,
"has_results": len(hierarchical_results) > 0,
}
try:
# Render template
rendered_prompt = await template_loader.render(
"prompts/continue_conversation.hbs", template_context
)
# Calculate metadata
# Count items of different types
observation_count = 0
relation_count = 0
entity_count = 0
# Get the hierarchical results from the template context
hierarchical_results_for_count = template_context.get("hierarchical_results", [])
# For topic-based search
if request.topic:
for item in hierarchical_results_for_count:
if hasattr(item, "observations"):
observation_count += len(item.observations) if item.observations else 0
if hasattr(item, "related_results"):
for related in item.related_results or []:
if hasattr(related, "type"):
if related.type == "relation":
relation_count += 1
elif related.type == "entity": # pragma: no cover
entity_count += 1 # pragma: no cover
# For recent activity
else:
for item in hierarchical_results_for_count:
if hasattr(item, "observations"):
observation_count += len(item.observations) if item.observations else 0
if hasattr(item, "related_results"):
for related in item.related_results or []:
if hasattr(related, "type"):
if related.type == "relation":
relation_count += 1
elif related.type == "entity": # pragma: no cover
entity_count += 1 # pragma: no cover
# Build metadata
metadata = {
"query": request.topic,
"timeframe": request.timeframe,
"search_count": len(search_results)
if request.topic
else 0, # Original search results count
"context_count": len(hierarchical_results_for_count),
"observation_count": observation_count,
"relation_count": relation_count,
"total_items": (
len(hierarchical_results_for_count)
+ observation_count
+ relation_count
+ entity_count
),
"search_limit": request.search_items_limit,
"context_depth": request.depth,
"related_limit": request.related_items_limit,
"generated_at": datetime.now(timezone.utc).isoformat(),
}
prompt_metadata = PromptMetadata(**metadata)
return PromptResponse(
prompt=rendered_prompt, context=template_context, metadata=prompt_metadata
)
except Exception as e:
logger.error(f"Error rendering continue conversation template: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error rendering prompt template: {str(e)}",
)
@router.post("/search", response_model=PromptResponse)
async def search_prompt(
search_service: SearchServiceDep,
entity_service: EntityServiceDep,
request: SearchPromptRequest,
page: int = 1,
page_size: int = 10,
) -> PromptResponse:
"""Generate a prompt for search results.
This endpoint takes a search query and formats the results into a helpful
prompt with context and suggestions.
Args:
request: The search parameters
page: The page number for pagination
page_size: The number of results per page, defaults to 10
Returns:
Formatted search results prompt with context
"""
logger.info(f"Generating search prompt, query: {request.query}, timeframe: {request.timeframe}")
limit = page_size
offset = (page - 1) * page_size
query = SearchQuery(text=request.query, after_date=request.timeframe)
results = await search_service.search(query, limit=limit, offset=offset)
search_results = await to_search_results(entity_service, results)
template_context = {
"query": request.query,
"timeframe": request.timeframe,
"results": search_results,
"has_results": len(search_results) > 0,
"result_count": len(search_results),
}
try:
# Render template
rendered_prompt = await template_loader.render("prompts/search.hbs", template_context)
# Build metadata
metadata = {
"query": request.query,
"timeframe": request.timeframe,
"search_count": len(search_results),
"context_count": len(search_results),
"observation_count": 0, # Search results don't include observations
"relation_count": 0, # Search results don't include relations
"total_items": len(search_results),
"search_limit": limit,
"context_depth": 0, # No context depth for basic search
"related_limit": 0, # No related items for basic search
"generated_at": datetime.now(timezone.utc).isoformat(),
}
prompt_metadata = PromptMetadata(**metadata)
return PromptResponse(
prompt=rendered_prompt, context=template_context, metadata=prompt_metadata
)
except Exception as e:
logger.error(f"Error rendering search template: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error rendering prompt template: {str(e)}",
)
@@ -1,225 +0,0 @@
"""Routes for getting entity content."""
import tempfile
from pathlib import Path
from typing import Annotated
from fastapi import APIRouter, HTTPException, BackgroundTasks, Body
from fastapi.responses import FileResponse, JSONResponse
from loguru import logger
from basic_memory.deps import (
ProjectConfigDep,
LinkResolverDep,
SearchServiceDep,
EntityServiceDep,
FileServiceDep,
EntityRepositoryDep,
)
from basic_memory.repository.search_repository import SearchIndexRow
from basic_memory.schemas.memory import normalize_memory_url
from basic_memory.schemas.search import SearchQuery, SearchItemType
from basic_memory.models.knowledge import Entity as EntityModel
from datetime import datetime
router = APIRouter(prefix="/resource", tags=["resources"])
def get_entity_ids(item: SearchIndexRow) -> set[int]:
match item.type:
case SearchItemType.ENTITY:
return {item.id}
case SearchItemType.OBSERVATION:
return {item.entity_id} # pyright: ignore [reportReturnType]
case SearchItemType.RELATION:
from_entity = item.from_id
to_entity = item.to_id # pyright: ignore [reportReturnType]
return {from_entity, to_entity} if to_entity else {from_entity} # pyright: ignore [reportReturnType]
case _: # pragma: no cover
raise ValueError(f"Unexpected type: {item.type}")
@router.get("/{identifier:path}")
async def get_resource_content(
config: ProjectConfigDep,
link_resolver: LinkResolverDep,
search_service: SearchServiceDep,
entity_service: EntityServiceDep,
file_service: FileServiceDep,
background_tasks: BackgroundTasks,
identifier: str,
page: int = 1,
page_size: int = 10,
) -> FileResponse:
"""Get resource content by identifier: name or permalink."""
logger.debug(f"Getting content for: {identifier}")
# Find single entity by permalink
entity = await link_resolver.resolve_link(identifier)
results = [entity] if entity else []
# pagination for multiple results
limit = page_size
offset = (page - 1) * page_size
# search using the identifier as a permalink
if not results:
# if the identifier contains a wildcard, use GLOB search
query = (
SearchQuery(permalink_match=identifier)
if "*" in identifier
else SearchQuery(permalink=identifier)
)
search_results = await search_service.search(query, limit, offset)
if not search_results:
raise HTTPException(status_code=404, detail=f"Resource not found: {identifier}")
# get the deduplicated entities related to the search results
entity_ids = {id for result in search_results for id in get_entity_ids(result)}
results = await entity_service.get_entities_by_id(list(entity_ids))
# return single response
if len(results) == 1:
entity = results[0]
file_path = Path(f"{config.home}/{entity.file_path}")
if not file_path.exists():
raise HTTPException(
status_code=404,
detail=f"File not found: {file_path}",
)
return FileResponse(path=file_path)
# for multiple files, initialize a temporary file for writing the results
with tempfile.NamedTemporaryFile(delete=False, mode="w", suffix=".md") as tmp_file:
temp_file_path = tmp_file.name
for result in results:
# Read content for each entity
content = await file_service.read_entity_content(result)
memory_url = normalize_memory_url(result.permalink)
modified_date = result.updated_at.isoformat()
checksum = result.checksum[:8] if result.checksum else ""
# Prepare the delimited content
response_content = f"--- {memory_url} {modified_date} {checksum}\n"
response_content += f"\n{content}\n"
response_content += "\n"
# Write content directly to the temporary file in append mode
tmp_file.write(response_content)
# Ensure all content is written to disk
tmp_file.flush()
# Schedule the temporary file to be deleted after the response
background_tasks.add_task(cleanup_temp_file, temp_file_path)
# Return the file response
return FileResponse(path=temp_file_path)
def cleanup_temp_file(file_path: str):
"""Delete the temporary file."""
try:
Path(file_path).unlink() # Deletes the file
logger.debug(f"Temporary file deleted: {file_path}")
except Exception as e: # pragma: no cover
logger.error(f"Error deleting temporary file {file_path}: {e}")
@router.put("/{file_path:path}")
async def write_resource(
config: ProjectConfigDep,
file_service: FileServiceDep,
entity_repository: EntityRepositoryDep,
search_service: SearchServiceDep,
file_path: str,
content: Annotated[str, Body()],
) -> JSONResponse:
"""Write content to a file in the project.
This endpoint allows writing content directly to a file in the project.
Also creates an entity record and indexes the file for search.
Args:
file_path: Path to write to, relative to project root
request: Contains the content to write
Returns:
JSON response with file information
"""
try:
# Get content from request body
# Ensure it's UTF-8 string content
if isinstance(content, bytes): # pragma: no cover
content_str = content.decode("utf-8")
else:
content_str = str(content)
# Get full file path
full_path = Path(f"{config.home}/{file_path}")
# Ensure parent directory exists
full_path.parent.mkdir(parents=True, exist_ok=True)
# Write content to file
checksum = await file_service.write_file(full_path, content_str)
# Get file info
file_stats = file_service.file_stats(full_path)
# Determine file details
file_name = Path(file_path).name
content_type = file_service.content_type(full_path)
entity_type = "canvas" if file_path.endswith(".canvas") else "file"
# Check if entity already exists
existing_entity = await entity_repository.get_by_file_path(file_path)
if existing_entity:
# Update existing entity
entity = await entity_repository.update(
existing_entity.id,
{
"title": file_name,
"entity_type": entity_type,
"content_type": content_type,
"file_path": file_path,
"checksum": checksum,
"updated_at": datetime.fromtimestamp(file_stats.st_mtime),
},
)
status_code = 200
else:
# Create a new entity model
entity = EntityModel(
title=file_name,
entity_type=entity_type,
content_type=content_type,
file_path=file_path,
checksum=checksum,
created_at=datetime.fromtimestamp(file_stats.st_ctime),
updated_at=datetime.fromtimestamp(file_stats.st_mtime),
)
entity = await entity_repository.add(entity)
status_code = 201
# Index the file for search
await search_service.index_entity(entity) # pyright: ignore
# Return success response
return JSONResponse(
status_code=status_code,
content={
"file_path": file_path,
"checksum": checksum,
"size": file_stats.st_size,
"created_at": file_stats.st_ctime,
"modified_at": file_stats.st_mtime,
},
)
except Exception as e: # pragma: no cover
logger.error(f"Error writing resource {file_path}: {e}")
raise HTTPException(status_code=500, detail=f"Failed to write resource: {str(e)}")
@@ -1,36 +0,0 @@
"""Router for search operations."""
from fastapi import APIRouter, BackgroundTasks
from basic_memory.api.routers.utils import to_search_results
from basic_memory.schemas.search import SearchQuery, SearchResponse
from basic_memory.deps import SearchServiceDep, EntityServiceDep
router = APIRouter(prefix="/search", tags=["search"])
@router.post("/", response_model=SearchResponse)
async def search(
query: SearchQuery,
search_service: SearchServiceDep,
entity_service: EntityServiceDep,
page: int = 1,
page_size: int = 10,
):
"""Search across all knowledge and documents."""
limit = page_size
offset = (page - 1) * page_size
results = await search_service.search(query, limit=limit, offset=offset)
search_results = await to_search_results(entity_service, results)
return SearchResponse(
results=search_results,
current_page=page,
page_size=page_size,
)
@router.post("/reindex")
async def reindex(background_tasks: BackgroundTasks, search_service: SearchServiceDep):
"""Recreate and populate the search index."""
await search_service.reindex_all(background_tasks=background_tasks)
return {"status": "ok", "message": "Reindex initiated"}
-130
View File
@@ -1,130 +0,0 @@
from typing import Optional, List
from basic_memory.repository import EntityRepository
from basic_memory.repository.search_repository import SearchIndexRow
from basic_memory.schemas.memory import (
EntitySummary,
ObservationSummary,
RelationSummary,
MemoryMetadata,
GraphContext,
ContextResult,
)
from basic_memory.schemas.search import SearchItemType, SearchResult
from basic_memory.services import EntityService
from basic_memory.services.context_service import (
ContextResultRow,
ContextResult as ServiceContextResult,
)
async def to_graph_context(
context_result: ServiceContextResult,
entity_repository: EntityRepository,
page: Optional[int] = None,
page_size: Optional[int] = None,
):
# Helper function to convert items to summaries
async def to_summary(item: SearchIndexRow | ContextResultRow):
match item.type:
case SearchItemType.ENTITY:
return EntitySummary(
title=item.title, # pyright: ignore
permalink=item.permalink,
content=item.content,
file_path=item.file_path,
created_at=item.created_at,
)
case SearchItemType.OBSERVATION:
return ObservationSummary(
title=item.title, # pyright: ignore
file_path=item.file_path,
category=item.category, # pyright: ignore
content=item.content, # pyright: ignore
permalink=item.permalink, # pyright: ignore
created_at=item.created_at,
)
case SearchItemType.RELATION:
from_entity = await entity_repository.find_by_id(item.from_id) # pyright: ignore
to_entity = await entity_repository.find_by_id(item.to_id) if item.to_id else None
return RelationSummary(
title=item.title, # pyright: ignore
file_path=item.file_path,
permalink=item.permalink, # pyright: ignore
relation_type=item.relation_type, # pyright: ignore
from_entity=from_entity.title, # pyright: ignore
to_entity=to_entity.title if to_entity else None,
created_at=item.created_at,
)
case _: # pragma: no cover
raise ValueError(f"Unexpected type: {item.type}")
# Process the hierarchical results
hierarchical_results = []
for context_item in context_result.results:
# Process primary result
primary_result = await to_summary(context_item.primary_result)
# Process observations
observations = []
for obs in context_item.observations:
observations.append(await to_summary(obs))
# Process related results
related = []
for rel in context_item.related_results:
related.append(await to_summary(rel))
# Add to hierarchical results
hierarchical_results.append(
ContextResult(
primary_result=primary_result,
observations=observations,
related_results=related,
)
)
# Create schema metadata from service metadata
metadata = MemoryMetadata(
uri=context_result.metadata.uri,
types=context_result.metadata.types,
depth=context_result.metadata.depth,
timeframe=context_result.metadata.timeframe,
generated_at=context_result.metadata.generated_at,
primary_count=context_result.metadata.primary_count,
related_count=context_result.metadata.related_count,
total_results=context_result.metadata.primary_count + context_result.metadata.related_count,
total_relations=context_result.metadata.total_relations,
total_observations=context_result.metadata.total_observations,
)
# Return new GraphContext with just hierarchical results
return GraphContext(
results=hierarchical_results,
metadata=metadata,
page=page,
page_size=page_size,
)
async def to_search_results(entity_service: EntityService, results: List[SearchIndexRow]):
search_results = []
for r in results:
entities = await entity_service.get_entities_by_id([r.entity_id, r.from_id, r.to_id]) # pyright: ignore
search_results.append(
SearchResult(
title=r.title, # pyright: ignore
type=r.type, # pyright: ignore
permalink=r.permalink,
score=r.score, # pyright: ignore
entity=entities[0].permalink if entities else None,
content=r.content,
file_path=r.file_path,
metadata=r.metadata,
category=r.category,
from_entity=entities[0].permalink if entities else None,
to_entity=entities[1].permalink if len(entities) > 1 else None,
relation_type=r.relation_type,
)
)
return search_results
-292
View File
@@ -1,292 +0,0 @@
"""Template loading and rendering utilities for the Basic Memory API.
This module handles the loading and rendering of Handlebars templates from the
templates directory, providing a consistent interface for all prompt-related
formatting needs.
"""
import textwrap
from typing import Dict, Any, Optional, Callable
from pathlib import Path
import json
import datetime
import pybars
from loguru import logger
# Get the base path of the templates directory
TEMPLATES_DIR = Path(__file__).parent.parent / "templates"
# Custom helpers for Handlebars
def _date_helper(this, *args):
"""Format a date using the given format string."""
if len(args) < 1: # pragma: no cover
return ""
timestamp = args[0]
format_str = args[1] if len(args) > 1 else "%Y-%m-%d %H:%M"
if hasattr(timestamp, "strftime"):
result = timestamp.strftime(format_str)
elif isinstance(timestamp, str):
try:
dt = datetime.datetime.fromisoformat(timestamp)
result = dt.strftime(format_str)
except ValueError:
result = timestamp
else:
result = str(timestamp) # pragma: no cover
return pybars.strlist([result])
def _default_helper(this, *args):
"""Return a default value if the given value is None or empty."""
if len(args) < 2: # pragma: no cover
return ""
value = args[0]
default_value = args[1]
result = default_value if value is None or value == "" else value
# Use strlist for consistent handling of HTML escaping
return pybars.strlist([str(result)])
def _capitalize_helper(this, *args):
"""Capitalize the first letter of a string."""
if len(args) < 1: # pragma: no cover
return ""
text = args[0]
if not text or not isinstance(text, str): # pragma: no cover
result = ""
else:
result = text.capitalize()
return pybars.strlist([result])
def _round_helper(this, *args):
"""Round a number to the specified number of decimal places."""
if len(args) < 1:
return ""
value = args[0]
decimal_places = args[1] if len(args) > 1 else 2
try:
result = str(round(float(value), int(decimal_places)))
except (ValueError, TypeError):
result = str(value)
return pybars.strlist([result])
def _size_helper(this, *args):
"""Return the size/length of a collection."""
if len(args) < 1:
return 0
value = args[0]
if value is None:
result = "0"
elif isinstance(value, (list, tuple, dict, str)):
result = str(len(value)) # pragma: no cover
else: # pragma: no cover
result = "0"
return pybars.strlist([result])
def _json_helper(this, *args):
"""Convert a value to a JSON string."""
if len(args) < 1: # pragma: no cover
return "{}"
value = args[0]
# For pybars, we need to return a SafeString to prevent HTML escaping
result = json.dumps(value) # pragma: no cover
# Safe string implementation to prevent HTML escaping
return pybars.strlist([result])
def _math_helper(this, *args):
"""Perform basic math operations."""
if len(args) < 3:
return pybars.strlist(["Math error: Insufficient arguments"])
lhs = args[0]
operator = args[1]
rhs = args[2]
try:
lhs = float(lhs)
rhs = float(rhs)
if operator == "+":
result = str(lhs + rhs)
elif operator == "-":
result = str(lhs - rhs)
elif operator == "*":
result = str(lhs * rhs)
elif operator == "/":
result = str(lhs / rhs)
else:
result = f"Unsupported operator: {operator}"
except (ValueError, TypeError) as e:
result = f"Math error: {e}"
return pybars.strlist([result])
def _lt_helper(this, *args):
"""Check if left hand side is less than right hand side."""
if len(args) < 2:
return False
lhs = args[0]
rhs = args[1]
try:
return float(lhs) < float(rhs)
except (ValueError, TypeError):
# Fall back to string comparison for non-numeric values
return str(lhs) < str(rhs)
def _if_cond_helper(this, options, condition):
"""Block helper for custom if conditionals."""
if condition:
return options["fn"](this)
elif "inverse" in options:
return options["inverse"](this)
return "" # pragma: no cover
def _dedent_helper(this, options):
"""Dedent a block of text to remove common leading whitespace.
Usage:
{{#dedent}}
This text will have its
common leading whitespace removed
while preserving relative indentation.
{{/dedent}}
"""
if "fn" not in options: # pragma: no cover
return ""
# Get the content from the block
content = options["fn"](this)
# Convert to string if it's a strlist
if (
isinstance(content, list)
or hasattr(content, "__iter__")
and not isinstance(content, (str, bytes))
):
content_str = "".join(str(item) for item in content) # pragma: no cover
else:
content_str = str(content) # pragma: no cover
# Add trailing and leading newlines to ensure proper dedenting
# This is critical for textwrap.dedent to work correctly with mixed content
content_str = "\n" + content_str + "\n"
# Use textwrap to dedent the content and remove the extra newlines we added
dedented = textwrap.dedent(content_str)[1:-1]
# Return as a SafeString to prevent HTML escaping
return pybars.strlist([dedented]) # pragma: no cover
class TemplateLoader:
"""Loader for Handlebars templates.
This class is responsible for loading templates from disk and rendering
them with the provided context data.
"""
def __init__(self, template_dir: Optional[str] = None):
"""Initialize the template loader.
Args:
template_dir: Optional custom template directory path
"""
self.template_dir = Path(template_dir) if template_dir else TEMPLATES_DIR
self.template_cache: Dict[str, Callable] = {}
self.compiler = pybars.Compiler()
# Set up standard helpers
self.helpers = {
"date": _date_helper,
"default": _default_helper,
"capitalize": _capitalize_helper,
"round": _round_helper,
"size": _size_helper,
"json": _json_helper,
"math": _math_helper,
"lt": _lt_helper,
"if_cond": _if_cond_helper,
"dedent": _dedent_helper,
}
logger.debug(f"Initialized template loader with directory: {self.template_dir}")
def get_template(self, template_path: str) -> Callable:
"""Get a template by path, using cache if available.
Args:
template_path: The path to the template, relative to the templates directory
Returns:
The compiled Handlebars template
Raises:
FileNotFoundError: If the template doesn't exist
"""
if template_path in self.template_cache:
return self.template_cache[template_path]
# Convert from Liquid-style path to Handlebars extension
if template_path.endswith(".liquid"):
template_path = template_path.replace(".liquid", ".hbs")
elif not template_path.endswith(".hbs"):
template_path = f"{template_path}.hbs"
full_path = self.template_dir / template_path
if not full_path.exists():
raise FileNotFoundError(f"Template not found: {full_path}")
with open(full_path, "r", encoding="utf-8") as f:
template_str = f.read()
template = self.compiler.compile(template_str)
self.template_cache[template_path] = template
logger.debug(f"Loaded template: {template_path}")
return template
async def render(self, template_path: str, context: Dict[str, Any]) -> str:
"""Render a template with the given context.
Args:
template_path: The path to the template, relative to the templates directory
context: The context data to pass to the template
Returns:
The rendered template as a string
"""
template = self.get_template(template_path)
return template(context, helpers=self.helpers)
def clear_cache(self) -> None:
"""Clear the template cache."""
self.template_cache.clear()
logger.debug("Template cache cleared")
# Global template loader instance
template_loader = TemplateLoader()
-1
View File
@@ -1 +0,0 @@
"""CLI tools for basic-memory"""
-73
View File
@@ -1,73 +0,0 @@
from typing import Optional
import typer
from basic_memory.config import get_project_config
from basic_memory.mcp.project_session import session
def version_callback(value: bool) -> None:
"""Show version and exit."""
if value: # pragma: no cover
import basic_memory
from basic_memory.config import config
typer.echo(f"Basic Memory version: {basic_memory.__version__}")
typer.echo(f"Current project: {config.project}")
typer.echo(f"Project path: {config.home}")
raise typer.Exit()
app = typer.Typer(name="basic-memory")
@app.callback()
def app_callback(
ctx: typer.Context,
project: Optional[str] = typer.Option(
None,
"--project",
"-p",
help="Specify which project to use 1",
envvar="BASIC_MEMORY_PROJECT",
),
version: Optional[bool] = typer.Option(
None,
"--version",
"-v",
help="Show version and exit.",
callback=version_callback,
is_eager=True,
),
) -> None:
"""Basic Memory - Local-first personal knowledge management."""
# Run initialization for every command unless --version was specified
if not version and ctx.invoked_subcommand is not None:
from basic_memory.config import app_config
from basic_memory.services.initialization import ensure_initialization
ensure_initialization(app_config)
# Initialize MCP session with the specified project or default
if project: # pragma: no cover
# Use the project specified via --project flag
current_project_config = get_project_config(project)
session.set_current_project(current_project_config.name)
# Update the global config to use this project
from basic_memory.config import update_current_project
update_current_project(project)
else:
# Use the default project
current_project = app_config.default_project
session.set_current_project(current_project)
# Register sub-command groups
import_app = typer.Typer(help="Import data from various sources")
app.add_typer(import_app, name="import")
claude_app = typer.Typer()
import_app.add_typer(claude_app, name="claude")
-18
View File
@@ -1,18 +0,0 @@
"""CLI commands for basic-memory."""
from . import auth, status, sync, db, import_memory_json, mcp, import_claude_conversations
from . import import_claude_projects, import_chatgpt, tool, project
__all__ = [
"auth",
"status",
"sync",
"db",
"import_memory_json",
"mcp",
"import_claude_conversations",
"import_claude_projects",
"import_chatgpt",
"tool",
"project",
]

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