Compare commits

...

242 Commits

Author SHA1 Message Date
phernandez 14c1fe4e89 chore: update version to 0.15.0 for v0.15.0 release 2025-10-04 15:02:28 -05:00
phernandez 367dc6962a style: apply ruff formatting to test files
Auto-format test files for permalink collision tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-10-04 15:00:19 -05:00
phernandez ee18eb2fea docs: add v0.15.0 changelog entry
Comprehensive changelog for v0.15.0 release covering:
- Critical permalink collision data loss fix
- 10+ bug fixes including #330, #329, #328, #312
- 9 new features including cloud sync and subscription validation
- Platform improvements (Python 3.13, Windows, Docker)
- Enhanced testing and documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-10-04 14:53:06 -05:00
phernandez 2a050edee4 fix: prevent permalink collision via strict link resolution
Fixes critical data loss bug where creating similar entity names
(e.g., "Node C") would overwrite existing entities (e.g., "Node A.md")
due to fuzzy search incorrectly matching similar file paths.

Changes:
- Add strict=True to resolve_link() calls in entity_service.py
- Disables fuzzy search fallback during entity creation/update
- Prevents false positive matches on similar paths like
  "edge-cases/Node A.md" and "edge-cases/Node C.md"

Testing:
- Added comprehensive integration test reproducing the bug scenario
- Added MCP-level permalink collision tests
- All 55 entity service tests pass
- Manual testing confirms fix prevents file overwrite

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-10-04 14:45:37 -05:00
Paul Hernandez f3b1945e4c fix: remove .env file loading from BasicMemoryConfig (#330)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-10-04 01:13:06 -05:00
Paul Hernandez 16d7eddbf7 ci: Add Python 3.13 to test matrix (#331)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-10-04 01:12:44 -05:00
Paul Hernandez f5a11f3911 fix: normalize underscores in memory:// URLs for build_context (#329)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-10-04 00:16:06 -05:00
Paul Hernandez ee83b0e5a8 fix: simplify entity upsert to use database-level conflict resolution (#328)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-03 23:25:34 -05:00
Paul Hernandez a7bf42ef49 fix: Add proper datetime JSON schema format annotations for MCP validation (#312)
Signed-off-by: Claude Code <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-10-03 22:47:02 -05:00
Paul Hernandez 903591384d feat: Add disable_permalinks config flag (#313)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-03 22:11:47 -05:00
Paul Hernandez 33ee1e0831 feat: integrate ignore_utils to skip .gitignored files in sync process (#314)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-03 21:39:31 -05:00
Paul Hernandez c83d567917 fix: enable WAL mode and add Windows-specific SQLite optimizations (#316)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-03 21:10:09 -05:00
Paul Hernandez ace6a0f50d feat: CLI Subscription Validation (SPEC-13 Phase 2) (#327)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-03 17:59:32 -05:00
Jonathan Nguyen fc38877008 Fix: Corrected dead links in README (#321)
Signed-off-by: Jonathan Nguyen <74562467+jonathan-d-nguyen@users.noreply.github.com>
2025-10-03 10:23:16 -05:00
Paul Hernandez 99a35a7fb4 feat: Cloud CLI cloud sync via rclone bisync (#322)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-03 10:18:43 -05:00
Paul Hernandez ea2e93d926 fix: rework lifecycle management to optimize cloud deployment (#320)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-28 15:11:38 -05:00
Paul Hernandez 324844a670 fix: resolve entity relations in background to prevent cold start blocking (#319)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-28 09:24:29 -05:00
Paul Hernandez f818702ab7 fix: enforce minimum 1-day timeframe for recent_activity to handle timezone issues (#318)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-27 23:37:00 -05:00
Paul Hernandez 2efd8f44e2 fix: critical cloud deployment fixes for MCP stability (#317)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-27 21:57:39 -05:00
Paul Hernandez 5da97e4820 feat: implement SPEC-11 API performance optimizations (#315)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-26 14:34:46 -05:00
Paul Hernandez 17a6733c9d fix: remove obsolete update_current_project function and --project flag reference (#310)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-09-26 11:46:33 -05:00
Drew Cain 3e168b98f3 fix: move_note without file extension (#281)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: phernandez <paul@basicmachines.co>
2025-09-26 11:41:45 -05:00
Paul Hernandez 1091e11322 fix: Make sync operations truly non-blocking with thread pool (#309)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-26 10:10:58 -05:00
Paul Hernandez f40ab31685 feat: chatgpt tools for search and fetch (#305)
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Drew Cain <groksrc@users.noreply.github.com>
2025-09-25 11:55:56 -05:00
phernandez bcf7f40979 fix: Correct GitHub workflow conditions for org member @claude mentions
Fixed the conditional logic in claude.yml to properly handle different event types:
- Use github.event.comment.author_association for issue_comment events
- Use github.event.sender.author_association for other events
- Maintain support for all basicmachines-co org members (OWNER/MEMBER/COLLABORATOR)

This ensures @claude mentions in PR comments trigger the workflow correctly.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-09-23 10:00:51 -05:00
Paul Hernandez 8c7e29e325 chore: Update Claude Code GitHub Workflow (#308)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-23 09:50:38 -05:00
phernandez 84c0b36dee feat: Add comprehensive cloud mount CLI commands and documentation
This commit implements SPEC-7 Phase 4 by adding local file access capabilities
to the Basic Memory Cloud CLI, enabling users to mount their cloud files locally
for real-time editing.

New features:
- Cloud mount setup with automatic rclone installation
- Mount/unmount/status commands with three performance profiles
- Cross-platform rclone installer with package manager fallbacks
- Mount configuration management with tenant-specific credentials
- Comprehensive documentation with examples and troubleshooting

Mount profiles:
- fast: 5s sync for active development
- balanced: 10-15s sync (recommended)
- safe: 15s+ sync with conflict detection

Technical implementation:
- Uses rclone NFS mount (no FUSE dependencies)
- Tigris object storage with scoped credentials
- Bidirectional sync with configurable cache settings
- Process management and cleanup

Fixes Python module conflict by moving cloud.py commands to cloud/core_commands.py
to resolve typer CLI loading issues with cloud.py file vs cloud/ directory.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-09-22 17:47:06 -05:00
Paul Hernandez 2c5c606a39 feat: Implement cloud mount CLI commands for local file access (#306)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-22 14:19:25 -05:00
Paul Hernandez a1d7792bdb feat: Implement SPEC-6 Stateless Architecture for MCP Tools (#298)
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Drew Cain <groksrc@users.noreply.github.com>
2025-09-21 20:39:19 -05:00
phernandez 7979b4192e remove no content-encoding: none header
Signed-off-by: phernandez <paul@basicmachines.co>
2025-09-16 16:35:32 -05:00
Drew Cain 52d9b3c752 setting content-encoding to none for mcp
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-09-16 16:09:33 -05:00
Paul Hernandez e0d8aeb149 feat: Basic memory cloud upload (#296)
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Drew Cain <groksrc@gmail.com>
2025-09-16 15:07:14 -05:00
Brandon Mayes 17b929446a fix: Sanitize folder names and properly join paths (#292) 2025-09-15 23:26:56 -04:00
Paul Hernandez b00e4ff5a1 fix: replace deprecated json_encoders with Pydantic V2 field serializers (#295)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-09-14 22:19:16 -05:00
Drew Cain 0499319ded fix: rename MCP prompt names to avoid slash command parsing issues (#289)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2025-09-09 21:42:35 -05:00
jope-bm 3a6baf80fc feat: Merge Cloud auth (#291)
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-09 14:48:17 -06:00
jope-bm ec2fa07350 chore: apply lint and formatting fixes for 0.14.4 release (#290)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-05 10:00:52 -06:00
Joe P 73cade27ab chore: update version to 0.14.4 for v0.14.4 release 2025-09-04 14:03:09 -06:00
Joe P 7e024a8674 fix: resolve linting errors for release preparation
- Replace bare except clauses with Exception in legal_file_inventory.py
- Remove unused variables in test files
- Prepare codebase for v0.14.4 release
2025-09-04 14:01:06 -06:00
Drew Cain 22f7bfa398 fix: Update YAML frontmatter tag formatting for Obsidian compatibility (#280)
Signed-off-by: Drew Cain <groksrc@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-09-04 09:57:45 -05:00
jope-bm cd7cee650f fix: complete project management special character support (#272) (#279)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-29 10:57:13 -06:00
Paul Hernandez 105bcaa025 feat: implement non-root Docker container to fix file ownership issues (#277)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Drew Cain <groksrc@gmail.com>
2025-08-28 22:15:14 -05:00
Brandon Mayes 74e12eb782 fix: Sanitize filenames and allow optional kebab case (#260)
Signed-off-by: Brandon Mayes <5610870+bdmayes@users.noreply.github.com>
2025-08-27 19:29:01 -04:00
Drew Cain 7a8b08d11e fix: Windows test failures and add Windows CI support (#273)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-25 08:58:24 -05:00
manuelbliemel 9aa40246a8 Addressed issues when running basic-memory on the Windows platform (#252)
Signed-off-by: Manuel Bliemel <manuel.bliemel@gmail.com>
2025-08-24 19:12:40 -07:00
jope-bm 7aff836c57 fix: Add ISO datetime serialization to MCP schema models (#270)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-23 07:23:07 -06:00
jope-bm 285e96baea fix: Fix observation parsing to exclude markdown and wiki links (#269)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-22 20:16:05 -06:00
jope-bm 2cd2a62f30 fix: Ensure all datetime operations return timezone-aware objects (#268)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-22 13:43:55 -06:00
jope-bm f3d8d8d617 fix: Use discriminated unions for MCP schema validation in build_context (#266)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
2025-08-22 09:36:32 -06:00
jope-bm 9743fcd13e fix: Respect BASIC_MEMORY_LOG_LEVEL and BASIC_MEMORY_CONSOLE_LOGGING environment variables (#264)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-22 07:58:19 -06:00
phernandez 65d1984a53 Update CLA.md to include copyright and license info
Signed-off-by: phernandez <paul@basicmachines.co>
2025-08-21 18:21:24 -05:00
jope-bm b814d40ab1 fix: Add project isolation to ContextService.find_related() method (#261) (#262)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 20:07:04 -06:00
Paul Hernandez 2438094914 fix: handle vim atomic write DELETE events without ADD (#249)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 14:36:43 -05:00
jope-bm 5d74d7407c fix: Enable string-to-integer conversion for build_context depth parameter (#259)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 11:22:14 -06:00
jope-bm b6aeb3217c fix: Add missing foreign key constraints for project removal (#254) (#258)
Signed-off-by: Joe P <joe@basicmemory.com>
Signed-off-by: joe@basicmemory.com
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 08:49:07 -06:00
jope-bm 08ee7e1201 fix: Critical search index bug - prevent note disappearing on edit (#257)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
2025-08-19 15:41:27 -06:00
phernandez 63ae9ee0e4 docs: Re-implement external documentation improvements
- Fix typo: 'enviroment' -> 'environment' in CLAUDE.md
- Update HTTP links to HTTPS in README.md
- Add comprehensive VS Code integration instructions
- Maintain correct internal link references

All improvements re-implemented by Basic Machines team for clean IP ownership.
2025-08-08 15:31:04 -05:00
phernandez 0e78751d34 revert: Remove external documentation changes for clean IP
Reverting changes by:
- Ikko Eltociear Ashimine (typo fix)
- Jason Noble (HTTPS links)
- Matias Forbord (link fix)
- Marc Baiza (VS Code instructions)

Will be re-implemented by Basic Machines team for clean IP ownership.
2025-08-08 15:26:29 -05:00
phernandez 59eae34dee fix: Update function name in error messages to use correct search_notes
Corrects error message templates to reference the actual search_notes function name for consistency.
2025-08-08 15:25:33 -05:00
phernandez b1e55e169e revert: Remove external function name fix for clean IP
Original contribution by Amadeusz Wieczorek will be re-implemented by Basic Machines team.
2025-08-08 15:24:58 -05:00
phernandez 173bff35c1 feat: Add Chinese character support to permalink generation
Preserves non-ASCII characters like Chinese in permalinks while maintaining
backward compatibility with ASCII-only processing. This re-implements
functionality that was contributed externally, now with Basic Machines authorship.
2025-08-08 15:24:23 -05:00
phernandez 629c8e47c9 revert: Remove external Chinese character fix for clean IP
Original contribution by andyxinweiminicloud will be re-implemented by Basic Machines team for clean IP ownership.
2025-08-08 15:23:28 -05:00
phernandez 9e4b8bca8f Add legal inventory documentation for IP analysis 2025-08-08 15:16:39 -05:00
Drew Cain b0cc559426 chore: update version to 0.14.3 for v0.14.3 release 2025-08-01 22:06:53 -05:00
Drew Cain 7460a938df fix: make case sensitivity test platform-aware
- Add platform detection to handle case-insensitive file systems
- Test now passes on macOS and Windows while maintaining Linux behavior
- Fixes test failure on case-insensitive file systems
2025-08-01 22:02:53 -05:00
Drew Cain 43fa5762a8 ruff checks
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-08-01 21:50:18 -05:00
Paul Hernandez fb1350b294 fix: enhance character conflict detection and error handling for sync operations (#201)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-08-01 21:35:56 -05:00
Paul Hernandez 7585a29c96 fix: replace recursive _traverse_messages with iterative approach to handle deep conversation threads (#235)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-08-01 21:34:11 -05:00
Drew Cain 752c78c379 chore: minor cleanup (#228)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-07-31 21:32:56 -05:00
jope-bm a4a3b1b689 fix: handle missing 'name' key in memory JSON import (#241)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
2025-07-28 14:52:15 -06:00
jope-bm 6361574a20 fix: basic memory home env var not respected when project path is changed. (#239)
Signed-off-by: Joe P <joe@basicmemory.com>
2025-07-28 14:50:47 -06:00
jope-bm 24a1d6195d fix: path traversal security vulnerability in mcp tools (#223)
Signed-off-by: Joe P <joe@basicmemory.com>
2025-07-15 09:05:11 -06:00
jope-bm a0cf62375d docs: improve virtual environment setup instructions (#222)
Co-authored-by: Claude <noreply@anthropic.com>
2025-07-10 10:18:43 -06:00
Paul Hernandez 473f70c949 chore: Cloud auth (#213)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-07-07 21:08:25 -05:00
phernandez 2c29dcc2b2 chore: update version to 0.14.2 for v0.14.2 release 2025-07-03 17:30:40 -05:00
phernandez 448210e552 docs: add v0.14.2 changelog entry
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-03 17:23:54 -05:00
Drew Cain 3621bb7b4d fix: MCP Error with MCP-Hub #204 (#212)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-07-03 16:57:43 -05:00
Drew Cain f80ac0ee72 fix: replace deprecated datetime.utcnow() with timezone-aware alternatives and suppress SQLAlchemy warnings (#211)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2025-07-03 16:57:30 -05:00
Drew Cain 23ddf1918c chore: update version to 0.14.1 for v0.14.1 release 2025-07-01 22:08:25 -05:00
Drew Cain 2aca19aa05 chore: apply ruff formatting 2025-07-01 22:05:17 -05:00
Drew Cain 827f7cf3e3 fix: constrain fastmcp version to prevent breaking changes (#203)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-07-01 22:01:59 -05:00
Drew Cain bd4f55158b fix: Problems with MCP #190 (#202)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-07-01 10:50:44 -05:00
Drew Cain 5360005122 feat: Add to cursor button (#200)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-07-01 09:17:48 -05:00
Drew Cain 39f811f8b5 Update README.md
Added Homebrew instructions to README.md

Signed-off-by: Drew Cain <groksrc@users.noreply.github.com>
2025-06-26 21:51:14 -05:00
phernandez 8e69c8b533 chore: update version to 0.14.0 for v0.14.0 release 2025-06-26 16:18:10 -05:00
phernandez 627a5c3c22 docs: add comprehensive v0.14.0 changelog entry
Add detailed changelog for v0.14.0 release including:
- Docker Container Registry migration to GitHub Container Registry
- Enhanced search documentation with comprehensive syntax examples
- Cross-project file management with intelligent boundary detection
- 8 major bug fixes with issue numbers and commit links
- Technical improvements and infrastructure enhancements
- Migration guide and installation instructions

Covers all changes since v0.13.7 with proper categorization and
user-facing descriptions for better release communication.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-26 16:15:11 -05:00
phernandez cd88945b22 remove v0.13.0 from changelog
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-26 16:11:05 -05:00
phernandez cd8e372f0a fix: add test coverage for optional permalink in EntityResponse schema
- Add comprehensive test for None permalink validation in EntityResponse
- Ensures schema properly handles markdown files without explicit permalinks
- Addresses GitHub issue #170 validation errors during edit operations
- Test validates that permalink=None doesn't cause ValidationError

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-26 15:58:49 -05:00
Paul Hernandez a589f8b894 feat: enhance search_notes tool documentation with comprehensive syntax examples (#186)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-26 15:51:58 -05:00
Paul Hernandez c2f4b632cf fix: preserve permalink when editing notes without frontmatter permalink (#184)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-06-26 15:35:31 -05:00
phernandez 46d102cef1 update tests for search_repository
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-26 14:30:10 -05:00
phernandez 8e4dc026ce chore: update version to 0.14.0b1 for v0.14.0b1 beta release 2025-06-26 14:08:32 -05:00
phernandez 7af8e198c2 style: fix linting errors in test assertions
Replace equality comparisons to False with 'not' for better style.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-26 14:06:51 -05:00
Paul Hernandez 12b51522bc fix: implement project-specific sync status checks for MCP tools (#183)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-26 13:54:26 -05:00
Paul Hernandez ac9e148bcc test: add more tests for search_repository (#181)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-26 13:45:07 -05:00
Paul Hernandez 546e3cd8db fix: handle Boolean search syntax with hyphenated terms (#180)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-26 12:41:32 -05:00
phernandez de4737cc22 fix: correct typo and update changelog command template
- Fix typo: <versuib> → <version>
- Update version examples to v0.14.0 format
- Improve template formatting clarity

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-26 11:09:32 -05:00
phernandez 77eefeb252 update test-live.md regression suite
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-26 10:25:48 -05:00
phernandez e5923a0378 allow web_search in claude github action
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-26 09:29:23 -05:00
phernandez 1bf348259b fix formatting on files
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-25 22:32:05 -05:00
phernandez 224e4bf9e4 fixes #164 revove log level from mcp_server.run()
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-25 22:31:47 -05:00
Drew Cain 9f1db23c78 fix: respect BASIC_MEMORY_HOME environment variable in Docker containers (#174)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2025-06-25 21:40:30 -05:00
Paul Hernandez db5ef7d35c feat: enhance move_note tool with cross-project detection and guidance (#161)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-06-25 12:57:59 -05:00
Paul Hernandez f50650763d fix: ensure permalinks are generated for entities with null permalinks during move operations (#162)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-06-25 12:57:44 -05:00
Drew Cain 8a065c32f4 fix: handle None from_entity in Context API RelationSummary (#166)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2025-06-25 12:57:31 -05:00
Drew Cain 2a3adc109a fix: scope entity queries by project_id in upsert_entity method (#168)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2025-06-24 00:02:18 -05:00
Drew Cain a52ce1c860 fix: only update Homebrew on stable releases
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-06-21 08:12:23 -05:00
phernandez 616c1f0710 feat: switch from Docker Hub to GitHub Container Registry
🏴 Fighting the power! No more $15/month Docker Hub fees.

- Use ghcr.io/basicmachines-co/basic-memory for container images
- Native GitHub integration with GITHUB_TOKEN (no external secrets)
- Update all documentation and examples to use GHCR
- Remove Docker Hub description update step (not needed for GHCR)
- Completely free solution for public repositories

Docker users can now:
docker pull ghcr.io/basicmachines-co/basic-memory:latest
2025-06-20 15:57:49 -05:00
Paul Hernandez 74847cc380 feat: implement Docker CI workflow for automated image publishing (#159)
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-20 15:42:55 -05:00
phernandez d3b6c85184 docs: add v0.13.8 changelog entry
Documents recent fixes and features including:
- Docker container support with volume mounting
- #151: Reset command project configuration fix
- #148: MCP/CLI project state consistency fix
- FastMCP compatibility improvements
- Comprehensive integration testing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-19 22:13:32 -05:00
Paul Hernandez af44941d5a fix: reset command now clears project configuration (#152)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-19 21:55:50 -05:00
Paul Hernandez 35e4f73ae8 fix: resolve project state inconsistency between MCP and CLI (#149)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-06-19 21:24:51 -05:00
Drew Cain 7be001ca68 fix: fastmcp deprecation warning (#150)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
Co-authored-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-06-19 19:59:17 -05:00
Paul Hernandez 3269a2f33a feat: add Docker container support with volume mounting (#131)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: phernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-19 19:57:30 -05:00
Drew Cain b8191d090f chore: update version to 0.13.7 for v0.13.7 release 2025-06-18 22:32:53 -05:00
Drew Cain 2ce8a8e4b0 feat: Automatically update Homebrew
Signed-off-by: Drew Cain <groksrc@users.noreply.github.com>
2025-06-18 22:00:02 -05:00
Drew Cain f8099cd004 feat: Automatically update Homebrew (#147)
Signed-off-by: Drew Cain <groksrc@users.noreply.github.com>
2025-06-18 21:54:49 -05:00
phernandez 688e0b0971 chore: update version to 0.13.6 for v0.13.6 release 2025-06-18 17:58:56 -05:00
phernandez ed09ea4ec7 docs: add git sign-off reminder to CLAUDE.md
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-18 17:56:24 -05:00
phernandez c85d9f74d7 docs: add v0.13.6 changelog entry
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-18 17:55:21 -05:00
Paul Hernandez 84d2aaf641 fix: eliminate redundant database migration initialization (#146)
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-18 17:32:20 -05:00
Paul Hernandez 7789864493 fix: add entity_type parameter to write_note MCP tool (#145)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-18 17:10:15 -05:00
Drew Cain c6215fd819 fix: UNIQUE constraint failed: entity.permalink issue #139 (#140)
Signed-off-by: Drew Cain <groksrc@users.noreply.github.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-18 15:03:11 -05:00
Drew Cain b4c26a6133 fix: correct spelling error "Chose" to "Choose" in continue_conversation prompt (#141)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-06-17 22:15:14 -05:00
phernandez 3fdce683d7 Update README with new website and community links
- Add new main website: https://basicmemory.com
- Add Discord community: https://discord.gg/tyvKNccgqN
- Add YouTube channel: https://www.youtube.com/@basicmachines-co
- Reorganize links section for better clarity

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-15 10:37:24 -05:00
phernandez 782cb2df28 update README.md and CLAUDE.md docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-12 14:24:37 -05:00
phernandez 56c875f137 chore: update version to 0.13.5 for v0.13.5 release 2025-06-11 22:02:56 -05:00
phernandez 5049de7e2d docs: add changelog entry for v0.13.5
- Renamed create_project to create_memory_project for namespace isolation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 22:01:10 -05:00
phernandez 49011768f7 fix: rename create_project to create_memory_project for namespace isolation
Continue the namespace isolation effort by renaming the create_project tool
to create_memory_project to avoid conflicts with other MCP servers.

Changes:
- Renamed @mcp.tool() decorator from 'create_project' to 'create_memory_project'
- Updated all test references to use the new tool name
- Tool functionality remains identical, only the name changed
- Part of broader effort to ensure Basic Memory tools have unique namespaced names

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 21:58:40 -05:00
phernandez bc3557f000 chore: update version to 0.13.4 for v0.13.4 release 2025-06-11 21:41:06 -05:00
phernandez 611f5cd305 docs: add changelog entry for v0.13.4
- Renamed list_projects to list_memory_projects for namespace isolation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 21:39:20 -05:00
phernandez 4ea392d284 fix: rename list_projects to list_memory_projects to avoid naming conflicts
The tool name 'list_projects' was too generic and could conflict with other MCP servers.
Renamed to 'list_memory_projects' for better specificity and namespace isolation.

Changes:
- Renamed @mcp.tool() decorator from 'list_projects' to 'list_memory_projects'
- Updated all test references to use the new tool name
- Tool functionality remains identical, only the name changed

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 21:37:48 -05:00
phernandez d491757980 docs: add changelog entries for v0.13.2 and v0.13.3
- v0.13.2: automated release management system with version control
- v0.13.3: case-insensitive project switching bug fixes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 19:33:48 -05:00
phernandez 7a69ca2c36 chore: update version to 0.13.3 for v0.13.3 release 2025-06-11 19:29:04 -05:00
phernandez 70a6ce3411 fix: resolve case-insensitive project switching issues
This commit fixes the persistent case-insensitive project switching bug
where switching to projects with different case variations would succeed
but subsequent operations would fail.

Key changes:
- Enhanced config manager with case-insensitive project lookup using permalinks
- Updated project management tools to handle both name and permalink matching
- Fixed API URL construction to use permalinks consistently
- Added comprehensive test coverage for case-insensitive operations
- Updated project service to support permalink-based lookups

The fix ensures that users can switch to projects using any case variation
(e.g., "personal", "Personal", "PERSONAL") and all subsequent operations
work correctly with the canonical project name.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 19:26:27 -05:00
phernandez 5b69fd65cd fix: resolve case-insensitive project switching database lookup issue
Fix project switching bug where case-insensitive matching worked but
caused database lookup failures for subsequent operations.

**Problem:**
- switch_project('personal') succeeded (case-insensitive matching)
- get_current_project() failed with 'Project personal not found'
- Session stored user input case instead of canonical database name

**Solution:**
- Find project by permalink (case-insensitive) in switch_project
- Store canonical project name from database in session
- Use canonical name for all API calls and responses

**Test Coverage:**
- Added comprehensive case-insensitive project switching tests
- Added tests for case preservation in project listings
- Added tests for session state consistency after case switching
- Added error handling tests for non-existent projects

**Files Changed:**
- src/basic_memory/mcp/tools/project_management.py: Fixed switch_project logic
- test-int/mcp/test_project_management_integration.py: Added test coverage

**Test Cases Now Passing:**
-  switch_project('personal') → finds 'Personal' project
-  get_current_project() → works with canonical name
-  Project summary shows stats correctly
-  Case-insensitive matching for all case variations
-  Error handling for non-existent projects

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 18:19:17 -05:00
phernandez 85a178a6b8 chore: update version to 0.13.2 for v0.13.2 release 2025-06-11 17:09:57 -05:00
phernandez e4b32d7bc9 feat: add automated release management system
- Add version management in __init__.py
- Add justfile targets for release and beta automation
- Create Claude command documentation for /release and /beta
- Implement comprehensive quality checks and validation
- Support automated version updates and git tagging

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 17:06:23 -05:00
phernandez 9590b934cf Merge branch 'main' of github.com:basicmachines-co/basic-memory 2025-06-11 16:55:48 -05:00
phernandez 735f239f9b chore: update CHANGELOG.md for v0.13.1 release
Add changelog entry for v0.13.1 patch release documenting:
- Fixed CLI project management commands (#129)
- Resolved case sensitivity issues in project switching (#127)
- API endpoint standardization and improved error handling
- Consistent project name handling using permalinks

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 16:14:04 -05:00
Paul Hernandez 3ee30e1f36 fix: project cli commands and case sensitivity when switching projects (#130)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-11 16:09:53 -05:00
phernandez ac401ea254 chore: prepare for v0.13.0 release by removing release notes file
The release notes content has been integrated into CHANGELOG.md.
Removing the standalone RELEASE_NOTES_v0.13.0.md file as it's no longer needed.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-11 08:36:30 -05:00
phernandez fb2fd62ed9 fix: resolve type error and prepare for v0.13.0b6 release
- Add type ignore comment for MCP prompt function call
- Function works correctly at runtime despite false positive type error
- All quality checks now passing
2025-06-09 15:38:59 -05:00
phernandez 126d1655e6 fix: simplify versioning for release workflow
- Use static API version 'v0' instead of dynamic package version
- Remove version verification step in release workflow
- Dynamic versioning handled by uv-dynamic-versioning at build time
2025-06-09 15:25:20 -05:00
phernandez 2abf626c46 fix: resolve unused variable lint warnings in tests
- Remove unused variables in test mock functions
- Clean up test code per ruff linting rules
2025-06-09 15:15:05 -05:00
phernandez ba8e3d112d chore: update dependencies for beta release
- fastmcp 2.7.0 -> 2.7.1
- automated dependency updates
2025-06-09 00:48:48 -05:00
phernandez 7108a7baf1 fix: resolve sync race conditions and search errors
- Add IntegrityError handling in entity_service.create_entity_from_markdown for file_path/permalink constraint violations
- Add IntegrityError handling in sync_service.sync_regular_file for concurrent sync race conditions
- Fix FTS "unknown special query" error when searching for wildcard "*" patterns
- Add comprehensive test coverage for race condition edge cases and error handling
- Gracefully handle concurrent sync processes with fallback to update operations

Fixes sync errors from beta testing including:
- "UNIQUE constraint failed: entity.file_path"
- "UNIQUE constraint failed: entity.permalink"
- "unknown special query" FTS errors

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 15:20:27 -05:00
phernandez 35884ef3a7 fix: update MCP tool/prompt/resource calls to use .fn attribute
FastMCP library changes now require calling decorated functions via the .fn attribute:
- Tools: @mcp.tool() functions return FunctionTool, call with tool.fn()
- Prompts: @mcp.prompt() functions return FunctionPrompt, call with prompt.fn()
- Resources: @mcp.resource() functions return FunctionResource, call with resource.fn()

Updated core files:
- view_note.py: read_note() → read_note.fn()
- read_note.py: search_notes() → search_notes.fn() (2 locations)
- tool.py: 6 MCP tool calls updated to use .fn
- recent_activity.py: recent_activity() → recent_activity.fn()
- project.py: project_info() → project_info.fn() with type ignore

Updated 100+ test files systematically to use .fn attribute and fixed mock targets.

All 869 tests now pass. Fixes view_note tool error in Claude Desktop.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:52:23 -05:00
phernandez 040be05a81 fix: normalize project names in config during startup
- Fix case sensitivity bug where config had "Personal" but database expected "personal"
- Add project name normalization in synchronize_projects() to use generate_permalink()
- Update config file with normalized names and log changes for user visibility
- Use proper permalink generation instead of hardcoded name.lower().replace()
- Add comprehensive tests for project name normalization scenarios

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 09:28:57 -05:00
phernandez c141d7d1e6 chore: update version to 0.13.0b5 for release 2025-06-05 17:08:07 -05:00
phernandez b73aeb5ed8 feat: add view_note tool for formatted artifacts
- Implement view_note tool for better note readability in Claude Desktop
- Display notes as formatted markdown artifacts with special instructions
- Extract titles from frontmatter or headings automatically
- Add comprehensive test suite with 100% coverage
- Include view_note in live testing plan and release notes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-05 16:57:11 -05:00
phernandez 9a0e0bd82d add view_note tool
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-05 16:30:09 -05:00
phernandez 117fa44ecf fix project info stats tests
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-05 15:51:11 -05:00
phernandez 69d7610d47 test coverage 100%
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-05 13:07:20 -05:00
phernandez f608cd13f1 add justfile instead of Makefile, add ignores to test coverage
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-05 12:26:31 -05:00
phernandez 2162ad57fe all tests passing
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-05 11:13:40 -05:00
phernandez dd6ca80716 fix link_resolver tests
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-04 23:57:01 -05:00
phernandez ae3eeb0cc1 add tool prompting and doc updates for strict mode in edit/move, and sync_status tool
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-04 23:46:48 -05:00
phernandez 602c55fe90 only allow edit_note, move_note using strict identifier match
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-04 23:37:29 -05:00
phernandez 91bfe2dc92 add sync status tool
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-04 22:24:10 -05:00
phernandez a3cae1064d add background migration task and status tool/prompt
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-04 16:52:12 -05:00
phernandez c5c70cb0f4 improve validation for memory:// urls, add examples to build_context
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-04 00:16:33 -05:00
phernandez 80ec860a1c remove coverage files
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-03 23:14:07 -05:00
phernandez f64d5b2152 improve error messages for tools
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-03 23:12:59 -05:00
phernandez 69a625acd1 fix search escape issues, and empty forward reference resolving for entities
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-03 18:09:41 -05:00
phernandez 53c29a37ca fix: resolve FTS5 search syntax errors with special characters
Enhances search term preparation to handle special characters gracefully while preserving functionality:

- Improves FTS5 query preparation with targeted special character handling
- Preserves boolean operators (AND, OR, NOT) without modification
- Quotes problematic characters that cause syntax errors
- Maintains wildcard patterns for legitimate use cases
- Adds comprehensive error handling with graceful fallback

Includes extensive test coverage:
- 10 new test cases for various search scenarios
- Programming terms (C++, function(), email@domain.com) now searchable
- Malformed syntax handled without crashes
- Boolean and wildcard functionality preserved

Fixes search crashes when users enter queries containing special characters.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 16:45:13 -05:00
phernandez d8c13bf1d3 fix project table unique constraint bug
Signed-off-by: phernandez <paul@basicmachines.co>
2025-06-03 14:23:15 -05:00
phernandez 3f70f5ed42 feat: add /project:test-live command for comprehensive real-world testing
Implements live testing suite that:
- Uses installed Basic Memory version via MCP
- Follows TESTING.md methodology systematically
- Records all observations in Basic Memory notes
- Tests all 5 phases: core, features, edge cases, workflows, stress
- Creates dedicated test project for isolation
- Documents bugs with reproduction steps
- Tracks performance metrics and UX insights
- Validates v0.13.0 features in real usage scenarios

This enables 'Basic Memory testing itself' - comprehensive integration
testing that creates living documentation of test results.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 09:40:52 -05:00
phernandez 569a3de80b feat: add comprehensive custom Claude Code slash commands
Adds custom slash commands for streamlined development workflow:

Release Management (/project:release:*):
- beta - Create beta releases with automated quality checks
- release - Create stable releases with comprehensive validation
- release-check - Pre-flight validation without making changes
- changelog - Generate changelog entries from commits

Development (/project:*):
- test-coverage - Run tests with detailed coverage analysis
- lint-fix - Comprehensive code quality fixes with auto-repair
- check-health - Project health assessment and metrics

Commands are organized in .claude/commands/ directory following Claude Code
conventions and provide structured automation for common development tasks.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 09:24:03 -05:00
phernandez ac08a8d024 fix: update FastMCP initialization for API changes
- Remove deprecated auth_server_provider parameter
- Use auth parameter correctly with OAuthProvider instead of AuthSettings
- Fixes type error after dependency updates

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 09:08:17 -05:00
phernandez c13d4b1511 chore: update dependencies via make update-deps
- Updated authlib and other dependencies to latest versions
- Includes setuptools import fix for runtime compatibility

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 08:53:56 -05:00
phernandez 5b85d33a99 fix: remove unused setuptools import causing runtime error
Fixes ModuleNotFoundError when basic-memory is installed in environments
without setuptools (common with uv tool installs)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 08:52:30 -05:00
phernandez e2bd3142dc docs: remove working documentation files
Clean up development docs before v0.13.0 beta release

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 08:45:37 -05:00
phernandez 40b7de9168 docs: add comprehensive v0.13.0 changelog entry
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 08:43:54 -05:00
phernandez 455a280707 Merge branch 'main' of github.com:basicmachines-co/basic-memory 2025-06-03 08:31:42 -05:00
Paul Hernandez 634486107b feat: v0.13.0 pre (#122)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-03 01:00:40 -05:00
phernandez 23d34289fd Merge branch 'main' of github.com:basicmachines-co/basic-memory 2025-05-28 14:46:45 -05:00
Paul Hernandez 5ec4087f7c feat: Add Claude Code GitHub Workflow (#120)
Co-authored-by: Claude <noreply@anthropic.com>
2025-05-28 14:45:33 -05:00
phernandez 71941c35dd Merge branch 'main' of github.com:basicmachines-co/basic-memory 2025-05-25 10:07:54 -05:00
bm-claudeai 020957cd76 feat: Multi-project support, OAuth authentication, and major improvements (#119)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-05-25 10:07:34 -05:00
phernandez b3af6aa685 Merge branch 'main' of github.com:basicmachines-co/basic-memory 2025-04-17 11:12:18 -05:00
semantic-release 9437a5f83b chore(release): 0.12.3 [skip ci] 2025-04-17 16:04:31 +00:00
phernandez 3c1cc346df fix: modify recent_activity args to be strings instead of enums
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-17 10:56:46 -05:00
phernandez 81616ab42e update CLAUDE.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-17 10:56:46 -05:00
phernandez 321471f29d update CLAUDE.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-17 10:17:47 -05:00
phernandez 73ea91fe0d fix: add extra logic for permalink generation with mixed Latin unicode and Chinese characters
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-17 10:00:40 -05:00
andyxinweiminicloud 03d4e97b90 Fix: preserve Chinese characters in permalinks 2025-04-17 09:03:35 -05:00
Paul Hernandez 98622a7a47 Update README.md
fix example json for passing --project arg

Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-04-16 07:50:12 -05:00
Amadeusz Wieczorek 54dfa08aba Update name of function search_notes in error output (#92)
Signed-off-by: Amadeusz Wieczorek <git@amadeusw.com>
2025-04-14 11:06:15 -05:00
semantic-release 9433065a57 chore(release): 0.12.2 [skip ci] 2025-04-08 16:27:42 +00:00
Paul Hernandez 2934176331 fix: utf8 for all file reads/write/open instead of default platform encoding (#91)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-08 10:55:01 -05:00
semantic-release ac89eb47cb chore(release): 0.12.1 [skip ci] 2025-04-07 22:47:23 +00:00
Paul Hernandez 78a3412bcf fix: run migrations and sync when starting mcp (#88) 2025-04-07 17:45:26 -05:00
Paul Hernandez fa314b5d2b Update README.md
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-04-06 20:29:00 -05:00
Paul Hernandez d2ac62a27c Update README.md
Signed-off-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-04-06 20:25:06 -05:00
semantic-release 7ce7a29f35 chore(release): 0.12.0 [skip ci] 2025-04-06 23:44:17 +00:00
Paul Hernandez 3f4d9e4d87 fix: write_note preserves frontmatter fields in content (#84) 2025-04-06 18:39:51 -05:00
Paul Hernandez 00c8633cfc feat: add watch to mcp process (#83) 2025-04-06 18:33:10 -05:00
Paul Hernandez 617e60bda4 feat: permalink enhancements (#82)
- Avoiding "useless permalink values" for files without metadata
- Enable permalinks to be updated on move via config setting
2025-04-06 14:54:59 -05:00
github-actions[bot] 6c19c9edf5 fix: [BUG] # character accumulation in markdown frontmatter tags prop (#79) 2025-04-05 23:07:53 -05:00
github-actions[bot] 9bff1f732e fix: [BUG] write_note Tool Fails to Update Existing Files in Some Situations. (#80) 2025-04-05 22:31:40 -05:00
Paul Hernandez 248214cb11 fix: set default mcp log level to ERROR (#81) 2025-04-05 22:27:29 -05:00
Jason Noble 40ea28b0bf docs: Updated basicmachines.co links to be https (#69) 2025-04-05 22:22:37 -05:00
Marc Baiza 43cbb7b38c docs: Add VS Code instructions to README (#76) 2025-04-05 22:18:56 -05:00
github-actions[bot] 7930ddb291 fix: [BUG] Some notes never exit "modified" status (#77) 2025-04-05 22:03:38 -05:00
phernandez 1844e58210 Add pull_request_target trigger to test workflow with explanatory comment 2025-04-05 20:30:05 -05:00
phernandez 7fbb5ebfc5 Update claude-code-github-action to v0.11.0 with backtick handling fix 2025-04-05 20:04:18 -05:00
phernandez 0e5a465b10 add claude-output to .gitignore
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-05 19:45:07 -05:00
github-actions[bot] 9d581cee13 fix: [BUG] Cursor has errors calling search tool (#78) 2025-04-05 19:12:23 -05:00
phernandez f58852954b Update to claude-code-github-action v0.10.0 to check comment author for org membership 2025-04-05 12:54:33 -05:00
phernandez edd426b99f Update to claude-code-github-action v0.9.0 with organization membership check fix 2025-04-05 12:42:35 -05:00
phernandez 6b4a421315 Update to claude-code-github-action v0.8.0 with PERSONAL_ACCESS_TOKEN support 2025-04-05 12:13:05 -05:00
phernandez 4632405446 Fix claude-code-actions.yml to use v0.7.0 of basicmachines-co action 2025-04-05 12:01:04 -05:00
phernandez 3a6d5ff58d update claude code actions
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-04 23:31:08 -05:00
phernandez 03de85ec32 add claude code github action workflow
Signed-off-by: phernandez <paul@basicmachines.co>
2025-04-04 21:20:40 -05:00
phernandez 084c91ecce update basic-memory dep version
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-30 17:46:44 -05:00
semantic-release 3d45227b72 chore(release): 0.11.0 [skip ci] 2025-03-29 00:19:46 +00:00
Paul Hernandez 069c0a21c6 feat: add bm command alias for basic-memory (#67)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-28 19:12:25 -05:00
Paul Hernandez b27827671d feat: rename search tool to search_notes (#66)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-28 19:11:35 -05:00
Paul Hernandez 0743ade5fc fix: just delete db for reset db instead of using migrations. (#65)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-28 19:11:11 -05:00
Paul Hernandez f1c95709cb fix: make logs for each process - mcp, sync, cli (#64)
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-28 19:10:43 -05:00
Matias Forbord 3c68b7d5dd docs: Update broken "Multiple Projects" link in README.md (#55) 2025-03-26 14:31:51 -05:00
semantic-release 3d0077990f chore(release): 0.10.1 [skip ci] 2025-03-25 18:12:28 +00:00
phernandez 731b502d36 docs: add help docs to mcp cli tools
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-25 13:01:11 -05:00
phernandez 2a881b1425 refactor: move project stats into projct subcommand
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-25 12:50:11 -05:00
phernandez 681af5d450 chore: remove duplicate code in entity_service.py from bad merge
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-25 12:27:37 -05:00
phernandez b35037eb86 fix broken merge
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-24 23:18:22 -05:00
phernandez b667bca5a2 fix test coverage and type checks
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-24 22:46:47 -05:00
phernandez e716946b44 fix: preserve custom frontmatter fields when updating notes
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>
2025-03-24 22:46:47 -05:00
phernandez 46c4fd2164 fix: make set_default_project also activate project for current session to fix #37
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>
2025-03-24 22:46:47 -05:00
phernandez 9c791259a0 \fix: improve tags handling in write_note tool to fix #38\n\nThis change makes the write_note tool more flexible with tag inputs by:\n1. Adding a parse_tags helper function to handle various input formats\n2. Removing explicit type annotation from the write_note signature\n3. Adding documentation on how to pass tags from external MCP clients\n4. Adding a test that reproduces the issue from bug report #38\n\nThe issue occurred in external MCP clients like Cursor where the tags\nparameter was causing type mismatch errors.\n\n\ud83e\udd16 Generated with [Claude Code](https://claude.ai/code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-24 22:46:47 -05:00
phernandez cc2cae72c1 fix: move ai_assistant_guide.md into package resources to fix #39
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>
2025-03-24 22:46:47 -05:00
phernandez 0a1fdd8f15 update docs
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-24 22:46:47 -05:00
phernandez 78f234b180 fix: preserve custom frontmatter fields when updating notes
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>
2025-03-24 19:50:38 -05:00
phernandez cbe72be10a fix: make set_default_project also activate project for current session to fix #37
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>
2025-03-24 18:56:51 -05:00
phernandez 0ade6b0603 \fix: improve tags handling in write_note tool to fix #38\n\nThis change makes the write_note tool more flexible with tag inputs by:\n1. Adding a parse_tags helper function to handle various input formats\n2. Removing explicit type annotation from the write_note signature\n3. Adding documentation on how to pass tags from external MCP clients\n4. Adding a test that reproduces the issue from bug report #38\n\nThe issue occurred in external MCP clients like Cursor where the tags\nparameter was causing type mismatch errors.\n\n\ud83e\udd16 Generated with [Claude Code](https://claude.ai/code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\ 2025-03-24 18:30:21 -05:00
phernandez 390ff9d31c fix: move ai_assistant_guide.md into package resources to fix #39
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>
2025-03-24 17:34:47 -05:00
phernandez 3ed74b4a4d update docs 2025-03-24 17:28:30 -05:00
Ikko Eltociear Ashimine dfaf0fea9c docs: update CLAUDE.md (#33)
fix spelling in CLAUDE.md: enviroment -> environment
Signed-off-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
2025-03-24 13:15:12 -05:00
phernandez b26afa927f docs: add mcp badge, update cli reference, llms-install.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-16 14:09:08 -05:00
phernandez 3806943f8c add star history to README.md
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-16 11:24:08 -05:00
phernandez 3bffb2e190 Add updated getting started docs and utf8 test
Signed-off-by: phernandez <paul@basicmachines.co>
2025-03-15 16:52:17 -05:00
344 changed files with 54767 additions and 11736 deletions
+154
View File
@@ -0,0 +1,154 @@
---
name: python-developer
description: Python backend developer specializing in FastAPI, DBOS workflows, and API implementation. Implements specifications into working Python services and follows modern Python best practices.
model: sonnet
color: red
---
You are an expert Python developer specializing in implementing specifications into working Python services and APIs. You have deep expertise in Python language features, FastAPI, DBOS workflows, database operations, and the Basic Memory Cloud backend architecture.
**Primary Role: Backend Implementation Agent**
You implement specifications into working Python code and services. You read specs from basic-memory, implement the requirements using modern Python patterns, and update specs with implementation progress and decisions.
**Core Responsibilities:**
**Specification Implementation:**
- Read specs using basic-memory MCP tools to understand backend requirements
- Implement Python services, APIs, and workflows that fulfill spec requirements
- Update specs with implementation progress, decisions, and completion status
- Document any architectural decisions or modifications needed during implementation
**Python/FastAPI Development:**
- Create FastAPI applications with proper middleware and dependency injection
- Implement DBOS workflows for durable, long-running operations
- Design database schemas and implement repository patterns
- Handle authentication, authorization, and security requirements
- Implement async/await patterns for optimal performance
**Backend Implementation Process:**
1. **Read Spec**: Use `mcp__basic-memory__read_note` to get spec requirements
2. **Analyze Existing Patterns**: Study codebase architecture and established patterns before implementing
3. **Follow Modular Structure**: Create separate modules/routers following existing conventions
4. **Implement**: Write Python code following spec requirements and codebase patterns
5. **Test**: Create tests that validate spec success criteria
6. **Update Spec**: Document completion and any implementation decisions
7. **Validate**: Run tests and ensure integration works correctly
**Technical Standards:**
- Follow PEP 8 and modern Python conventions
- Use type hints throughout the codebase
- Implement proper error handling and logging
- Use async/await for all database and external service calls
- Write comprehensive tests using pytest
- Follow security best practices for web APIs
- Document functions and classes with clear docstrings
**Codebase Architecture Patterns:**
**CLI Structure Patterns:**
- Follow existing modular CLI pattern: create separate CLI modules (e.g., `upload_cli.py`) instead of adding commands directly to `main.py`
- Existing examples: `polar_cli.py`, `tenant_cli.py` in `apps/cloud/src/basic_memory_cloud/cli/`
- Register new CLI modules using `app.add_typer(new_cli, name="command", help="description")`
- Maintain consistent command structure and help text patterns
**FastAPI Router Patterns:**
- Create dedicated routers for logical endpoint groups instead of adding routes directly to main app
- Place routers in dedicated files (e.g., `apps/api/src/basic_memory_cloud_api/routers/webdav_router.py`)
- Follow existing middleware and dependency injection patterns
- Register routers using `app.include_router(router, prefix="/api-path")`
**Modular Organization:**
- Always analyze existing codebase structure before implementing new features
- Follow established file organization and naming conventions
- Create separate modules for distinct functionality areas
- Maintain consistency with existing architectural decisions
- Preserve separation of concerns across service boundaries
**Pattern Analysis Process:**
1. Examine similar existing functionality in the codebase
2. Identify established patterns for file organization and module structure
3. Follow the same architectural approach for consistency
4. Create new modules/routers following existing conventions
5. Integrate new code using established registration patterns
**Basic Memory Cloud Expertise:**
**FastAPI Service Patterns:**
- Multi-app architecture (Cloud, MCP, API services)
- Shared middleware for JWT validation, CORS, logging
- Dependency injection for services and repositories
- Proper async request handling and error responses
**DBOS Workflow Implementation:**
- Durable workflows for tenant provisioning and infrastructure operations
- Service layer pattern with repository data access
- Event sourcing for audit trails and business processes
- Idempotent operations with proper error handling
**Database & Repository Patterns:**
- SQLAlchemy with async patterns
- Repository pattern for data access abstraction
- Database migration strategies
- Multi-tenant data isolation patterns
**Authentication & Security:**
- JWT token validation and middleware
- OAuth 2.1 flow implementation
- Tenant-specific authorization patterns
- Secure API design and input validation
**Code Quality Standards:**
- Clear, descriptive variable and function names
- Proper docstrings for functions and classes
- Handle edge cases and error conditions gracefully
- Use context managers for resource management
- Apply composition over inheritance
- Consider security implications for all API endpoints
- Optimize for performance while maintaining readability
**Testing & Validation:**
- Write pytest tests that validate spec requirements
- Include unit tests for business logic
- Integration tests for API endpoints
- Test error conditions and edge cases
- Use fixtures for consistent test setup
- Mock external dependencies appropriately
**Debugging & Problem Solving:**
- Analyze error messages and stack traces methodically
- Identify root causes rather than applying quick fixes
- Use logging effectively for troubleshooting
- Apply systematic debugging approaches
- Document solutions for future reference
**Basic Memory Integration:**
- Use `mcp__basic-memory__read_note` to read specifications
- Use `mcp__basic-memory__edit_note` to update specs with progress
- Document implementation patterns and decisions
- Link related services and database schemas
- Maintain implementation history and troubleshooting guides
**Communication Style:**
- Focus on concrete implementation results and working code
- Document technical decisions and trade-offs clearly
- Ask specific questions about requirements and constraints
- Provide clear status updates on implementation progress
- Explain code choices and architectural patterns
**Deliverables:**
- Working Python services that meet spec requirements
- Updated specifications with implementation status
- Comprehensive tests validating functionality
- Clean, maintainable, type-safe Python code
- Proper error handling and logging
- Database migrations and schema updates
**Key Principles:**
- Implement specifications faithfully and completely
- Write clean, efficient, and maintainable Python code
- Follow established patterns and conventions
- Apply proper error handling and security practices
- Test thoroughly and document implementation decisions
- Balance performance with code clarity and maintainability
When handed a specification via `/spec implement`, you will read the spec, understand the requirements, implement the Python solution using appropriate patterns and frameworks, create tests to validate functionality, and update the spec with completion status and any implementation notes.
+126
View File
@@ -0,0 +1,126 @@
---
name: system-architect
description: System architect who designs and implements architectural solutions, creates ADRs, and applies software engineering principles to solve complex system design problems.
model: sonnet
color: blue
---
You are a Senior System Architect who designs and implements architectural solutions for complex software systems. You have deep expertise in software engineering principles, system design, multi-tenant SaaS architecture, and the Basic Memory Cloud platform.
**Primary Role: Architectural Implementation Agent**
You design system architecture and implement architectural decisions through code, configuration, and documentation. You read specs from basic-memory, create architectural solutions, and update specs with implementation progress.
**Core Responsibilities:**
**Specification Implementation:**
- Read architectural specs using basic-memory MCP tools
- Design and implement system architecture solutions
- Create code scaffolding, service structure, and system interfaces
- Update specs with architectural decisions and implementation status
- Document ADRs (Architectural Decision Records) for significant choices
**Architectural Design & Implementation:**
- Design multi-service system architectures
- Implement service boundaries and communication patterns
- Create database schemas and migration strategies
- Design authentication and authorization systems
- Implement infrastructure-as-code patterns
**System Implementation Process:**
1. **Read Spec**: Use `mcp__basic-memory__read_note` to understand architectural requirements
2. **Design Solution**: Apply architectural principles and patterns
3. **Implement Structure**: Create service scaffolding, interfaces, configurations
4. **Document Decisions**: Create ADRs documenting architectural choices
5. **Update Spec**: Record implementation progress and decisions
6. **Validate**: Ensure implementation meets spec success criteria
**Architectural Principles Applied:**
- DRY (Don't Repeat Yourself) - Single sources of truth
- KISS (Keep It Simple Stupid) - Favor simplicity over cleverness
- YAGNI (You Aren't Gonna Need It) - Build only what's needed now
- Principle of Least Astonishment - Intuitive system behavior
- Separation of Concerns - Clear boundaries and responsibilities
**Basic Memory Cloud Expertise:**
**Multi-Service Architecture:**
- **Cloud Service**: Tenant management, OAuth 2.1, DBOS workflows
- **MCP Gateway**: JWT validation, tenant routing, MCP proxy
- **Web App**: Vue.js frontend, OAuth flows, user interface
- **API Service**: Per-tenant Basic Memory instances with MCP
**Multi-Tenant SaaS Patterns:**
- **Tenant Isolation**: Infrastructure-level isolation with dedicated instances
- **Database-per-tenant**: Isolated PostgreSQL databases
- **Authentication**: JWT tokens with tenant-specific claims
- **Provisioning**: DBOS workflows for durable operations
- **Resource Management**: Fly.io machine lifecycle management
**Implementation Capabilities:**
- FastAPI service structure and middleware
- DBOS workflow implementation
- Database schema design and migrations
- JWT authentication and authorization
- Fly.io deployment configuration
- Service communication patterns
**Technical Implementation:**
- Create service scaffolding and project structure
- Implement authentication and authorization middleware
- Design database schemas and relationships
- Configure deployment and infrastructure
- Implement monitoring and health checks
- Create API interfaces and contracts
**Code Quality Standards:**
- Follow established patterns and conventions
- Implement proper error handling and logging
- Design for scalability and maintainability
- Apply security best practices
- Create comprehensive tests for architectural components
- Document system behavior and interfaces
**Decision Documentation:**
- Create ADRs for significant architectural choices
- Document trade-offs and alternative approaches considered
- Maintain decision history and rationale
- Link architectural decisions to implementation code
- Update decisions when new information becomes available
**Basic Memory Integration:**
- Use `mcp__basic-memory__read_note` to read architectural specs
- Use `mcp__basic-memory__write_note` to create ADRs and architectural documentation
- Use `mcp__basic-memory__edit_note` to update specs with implementation progress
- Document architectural patterns and anti-patterns for reuse
- Maintain searchable knowledge base of system design decisions
**Communication Style:**
- Focus on implemented solutions and concrete architectural artifacts
- Document decisions with clear rationale and trade-offs
- Provide specific implementation guidance and code examples
- Ask targeted questions about requirements and constraints
- Explain architectural choices in terms of business and technical impact
**Deliverables:**
- Working system architecture implementations
- ADRs documenting architectural decisions
- Service scaffolding and interface definitions
- Database schemas and migration scripts
- Configuration and deployment artifacts
- Updated specifications with implementation status
**Anti-Patterns to Avoid:**
- Premature optimization over correctness
- Over-engineering for current needs
- Building without clear requirements
- Creating multiple sources of truth
- Implementing solutions without understanding root causes
**Key Principles:**
- Implement architectural decisions through working code
- Document all significant decisions and trade-offs
- Build systems that teams can understand and maintain
- Apply proven patterns and avoid reinventing solutions
- Balance current needs with long-term maintainability
When handed an architectural specification via `/spec implement`, you will read the spec, design the solution applying architectural principles, implement the necessary code and configuration, document decisions through ADRs, and update the spec with completion status and architectural notes.
+95
View File
@@ -0,0 +1,95 @@
# /beta - Create Beta Release
Create a new beta release using the automated justfile target with quality checks and tagging.
## Usage
```
/beta <version>
```
**Parameters:**
- `version` (required): Beta version like `v0.13.2b1` or `v0.13.2rc1`
## Implementation
You are an expert release manager for the Basic Memory project. When the user runs `/beta`, execute the following steps:
### Step 1: Pre-flight Validation
1. Verify version format matches `v\d+\.\d+\.\d+(b\d+|rc\d+)` pattern
2. Check current git status for uncommitted changes
3. Verify we're on the `main` branch
4. Confirm no existing tag with this version
### Step 2: Use Justfile Automation
Execute the automated beta release process:
```bash
just beta <version>
```
The justfile target handles:
- ✅ Beta version format validation (supports b1, b2, rc1, etc.)
- ✅ Git status and branch checks
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
- ✅ Version update in `src/basic_memory/__init__.py`
- ✅ Automatic commit with proper message
- ✅ Tag creation and pushing to GitHub
- ✅ Beta release workflow trigger
### Step 3: Monitor Beta Release
1. Check GitHub Actions workflow starts successfully
2. Monitor workflow at: https://github.com/basicmachines-co/basic-memory/actions
3. Verify PyPI pre-release publication
4. Test beta installation: `uv tool install basic-memory --pre`
### Step 4: Beta Testing Instructions
Provide users with beta testing instructions:
```bash
# Install/upgrade to beta
uv tool install basic-memory --pre
# Or upgrade existing installation
uv tool upgrade basic-memory --prerelease=allow
```
## Version Guidelines
- **First beta**: `v0.13.2b1`
- **Subsequent betas**: `v0.13.2b2`, `v0.13.2b3`, etc.
- **Release candidates**: `v0.13.2rc1`, `v0.13.2rc2`, etc.
- **Final release**: `v0.13.2` (use `/release` command)
## Error Handling
- If `just beta` fails, examine the error output for specific issues
- If quality checks fail, fix issues and retry
- If version format is invalid, correct and retry
- If tag already exists, increment version number
## Success Output
```
✅ Beta Release v0.13.2b1 Created Successfully!
🏷️ Tag: v0.13.2b1
🚀 GitHub Actions: Running
📦 PyPI: Will be available in ~5 minutes as pre-release
Install/test with:
uv tool install basic-memory --pre
Monitor release: https://github.com/basicmachines-co/basic-memory/actions
```
## Beta Testing Workflow
1. **Create beta**: Use `/beta v0.13.2b1`
2. **Test features**: Install and validate new functionality
3. **Fix issues**: Address bugs found during testing
4. **Iterate**: Create `v0.13.2b2` if needed
5. **Release candidate**: Create `v0.13.2rc1` when stable
6. **Final release**: Use `/release v0.13.2` when ready
## Context
- Beta releases are pre-releases for testing new features
- Automatically published to PyPI with pre-release flag
- Uses the automated justfile target for consistency
- Version is automatically updated in `__init__.py`
- Ideal for validating changes before stable release
- Supports both beta (b1, b2) and release candidate (rc1, rc2) versions
+160
View File
@@ -0,0 +1,160 @@
# /changelog - Generate or Update Changelog Entry
Analyze commits and generate formatted changelog entry for a version.
## Usage
```
/changelog <version> [type]
```
**Parameters:**
- `version` (required): Version like `v0.14.0` or `v0.14.0b1`
- `type` (optional): `beta`, `rc`, or `stable` (default: `stable`)
## Implementation
You are an expert technical writer for the Basic Memory project. When the user runs `/changelog`, execute the following steps:
### Step 1: Version Analysis
1. **Determine Commit Range**
```bash
# Find last release tag
git tag -l "v*" --sort=-version:refname | grep -v "b\|rc" | head -1
# Get commits since last release
git log --oneline ${last_tag}..HEAD
```
2. **Parse Conventional Commits**
- Extract feat: (features)
- Extract fix: (bug fixes)
- Extract BREAKING CHANGE: (breaking changes)
- Extract chore:, docs:, test: (other improvements)
### Step 2: Categorize Changes
1. **Features (feat:)**
- New MCP tools
- New CLI commands
- New API endpoints
- Major functionality additions
2. **Bug Fixes (fix:)**
- User-facing bug fixes
- Critical issues resolved
- Performance improvements
- Security fixes
3. **Technical Improvements**
- Test coverage improvements
- Code quality enhancements
- Dependency updates
- Documentation updates
4. **Breaking Changes**
- API changes
- Configuration changes
- Behavior changes
- Migration requirements
### Step 3: Generate Changelog Entry
Create formatted entry following existing CHANGELOG.md style:
Example:
```markdown
## <version> (<date>)
### Features
- **Multi-Project Management System** - Switch between projects instantly during conversations
([`993e88a`](https://github.com/basicmachines-co/basic-memory/commit/993e88a))
- Instant project switching with session context
- Project-specific operations and isolation
- Project discovery and management tools
- **Advanced Note Editing** - Incremental editing with append, prepend, find/replace, and section operations
([`6fc3904`](https://github.com/basicmachines-co/basic-memory/commit/6fc3904))
- `edit_note` tool with multiple operation types
- Smart frontmatter-aware editing
- Validation and error handling
### Bug Fixes
- **#118**: Fix YAML tag formatting to follow standard specification
([`2dc7e27`](https://github.com/basicmachines-co/basic-memory/commit/2dc7e27))
- **#110**: Make --project flag work consistently across CLI commands
([`02dd91a`](https://github.com/basicmachines-co/basic-memory/commit/02dd91a))
### Technical Improvements
- **Comprehensive Testing** - 100% test coverage with integration testing
([`468a22f`](https://github.com/basicmachines-co/basic-memory/commit/468a22f))
- MCP integration test suite
- End-to-end testing framework
- Performance and edge case validation
### Breaking Changes
- **Database Migration**: Automatic migration from per-project to unified database.
Data will be re-index from the filesystem, resulting in no data loss.
- **Configuration Changes**: Projects now synced between config.json and database
- **Full Backward Compatibility**: All existing setups continue to work seamlessly
```
### Step 4: Integration
1. **Update CHANGELOG.md**
- Insert new entry at top
- Maintain consistent formatting
- Include commit links and issue references
2. **Validation**
- Check all major changes are captured
- Verify commit links work
- Ensure issue numbers are correct
## Smart Analysis Features
### Automatic Classification
- Detect feature additions from file changes
- Identify bug fixes from commit messages
- Find breaking changes from code analysis
- Extract issue numbers from commit messages
### Content Enhancement
- Add context for technical changes
- Include migration guidance for breaking changes
- Suggest installation/upgrade instructions
- Link to relevant documentation
## Output Format
### For Beta Releases
Example:
```markdown
## v0.13.0b4 (2025-06-03)
### Beta Changes Since v0.13.0b3
- Fix FastMCP API compatibility issues
- Update dependencies to latest versions
- Resolve setuptools import error
### Installation
```bash
uv tool install basic-memory --prerelease=allow
```
### Known Issues
- [List any known issues for beta testing]
```
### For Stable Releases
Full changelog with complete feature list, organized by impact and category.
## Context
- Follows existing CHANGELOG.md format and style
- Uses conventional commit standards
- Includes GitHub commit links for traceability
- Focuses on user-facing changes and value
- Maintains consistency with previous entries
+131
View File
@@ -0,0 +1,131 @@
# /release-check - Pre-flight Release Validation
Comprehensive pre-flight check for release readiness without making any changes.
## Usage
```
/release-check [version]
```
**Parameters:**
- `version` (optional): Version to validate like `v0.13.0`. If not provided, determines from context.
## Implementation
You are an expert QA engineer for the Basic Memory project. When the user runs `/release-check`, execute the following validation steps:
### Step 1: Environment Validation
1. **Git Status Check**
- Verify working directory is clean
- Confirm on `main` branch
- Check if ahead/behind origin
2. **Version Validation**
- Validate version format if provided
- Check for existing tags with same version
- Verify version increments properly from last release
### Step 2: Code Quality Gates
1. **Test Suite Validation**
```bash
just test
```
- All tests must pass
- Check test coverage (target: 95%+)
- Validate no skipped critical tests
2. **Code Quality Checks**
```bash
just lint
just type-check
```
- No linting errors
- No type checking errors
- Code formatting is consistent
### Step 3: Documentation Validation
1. **Changelog Check**
- CHANGELOG.md contains entry for target version
- Entry includes all major features and fixes
- Breaking changes are documented
2. **Documentation Currency**
- README.md reflects current functionality
- CLI reference is up to date
- MCP tools are documented
### Step 4: Dependency Validation
1. **Security Scan**
- No known vulnerabilities in dependencies
- All dependencies are at appropriate versions
- No conflicting dependency versions
2. **Build Validation**
- Package builds successfully
- All required files are included
- No missing dependencies
### Step 5: Issue Tracking Validation
1. **GitHub Issues Check**
- No critical open issues blocking release
- All milestone issues are resolved
- High-priority bugs are fixed
2. **Testing Coverage**
- Integration tests pass
- MCP tool tests pass
- Cross-platform compatibility verified
## Report Format
Generate a comprehensive report:
```
🔍 Release Readiness Check for v0.13.0
✅ PASSED CHECKS:
├── Git status clean
├── On main branch
├── All tests passing (744/744)
├── Test coverage: 98.2%
├── Type checking passed
├── Linting passed
├── CHANGELOG.md updated
└── No critical issues open
⚠️ WARNINGS:
├── 2 medium-priority issues still open
└── Documentation could be updated
❌ BLOCKING ISSUES:
└── None found
🎯 RELEASE READINESS: ✅ READY
Recommended next steps:
1. Address warnings if desired
2. Run `/release v0.13.0` when ready
```
## Validation Criteria
### Must Pass (Blocking)
- [ ] All tests pass
- [ ] No type errors
- [ ] No linting errors
- [ ] Working directory clean
- [ ] On main branch
- [ ] CHANGELOG.md has version entry
- [ ] No critical open issues
### Should Pass (Warnings)
- [ ] Test coverage >95%
- [ ] No medium-priority open issues
- [ ] Documentation up to date
- [ ] No dependency vulnerabilities
## Context
- This is a read-only validation - makes no changes
- Provides confidence before running actual release
- Helps identify issues early in release process
- Can be run multiple times safely
+92
View File
@@ -0,0 +1,92 @@
# /release - Create Stable Release
Create a stable release using the automated justfile target with comprehensive validation.
## Usage
```
/release <version>
```
**Parameters:**
- `version` (required): Release version like `v0.13.2`
## Implementation
You are an expert release manager for the Basic Memory project. When the user runs `/release`, execute the following steps:
### Step 1: Pre-flight Validation
1. Verify version format matches `v\d+\.\d+\.\d+` pattern
2. Check current git status for uncommitted changes
3. Verify we're on the `main` branch
4. Confirm no existing tag with this version
#### Documentation Validation
1. **Changelog Check**
- CHANGELOG.md contains entry for target version
- Entry includes all major features and fixes
- Breaking changes are documented
### Step 2: Use Justfile Automation
Execute the automated release process:
```bash
just release <version>
```
The justfile target handles:
- ✅ Version format validation
- ✅ Git status and branch checks
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
- ✅ Version update in `src/basic_memory/__init__.py`
- ✅ Automatic commit with proper message
- ✅ Tag creation and pushing to GitHub
- ✅ Release workflow trigger
### Step 3: Monitor Release Process
1. Check that GitHub Actions workflow starts successfully
2. Monitor workflow completion at: https://github.com/basicmachines-co/basic-memory/actions
3. Verify PyPI publication
4. Test installation: `uv tool install basic-memory`
### Step 4: Post-Release Validation
1. Verify GitHub release is created automatically
2. Check PyPI publication
3. Validate release assets
4. Update any post-release documentation
## Pre-conditions Check
Before starting, verify:
- [ ] All beta testing is complete
- [ ] Critical bugs are fixed
- [ ] Breaking changes are documented
- [ ] CHANGELOG.md is updated (if needed)
- [ ] Version number follows semantic versioning
## Error Handling
- If `just release` fails, examine the error output for specific issues
- If quality checks fail, fix issues and retry
- If changelog entry missing, update CHANGELOG.md and commit before retrying
- If GitHub Actions fail, check workflow logs for debugging
## Success Output
```
🎉 Stable Release v0.13.2 Created Successfully!
🏷️ Tag: v0.13.2
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.2
📦 PyPI: https://pypi.org/project/basic-memory/0.13.2/
🚀 GitHub Actions: Completed
Install with:
uv tool install basic-memory
Users can now upgrade:
uv tool upgrade basic-memory
```
## Context
- This creates production releases used by end users
- Must pass all quality gates before proceeding
- Uses the automated justfile target for consistency
- Version is automatically updated in `__init__.py`
- Triggers automated GitHub release with changelog
- Leverages uv-dynamic-versioning for package version management
+55
View File
@@ -0,0 +1,55 @@
---
allowed-tools: mcp__basic-memory__write_note, mcp__basic-memory__read_note, mcp__basic-memory__search_notes, mcp__basic-memory__edit_note, Task
argument-hint: [create|status|implement|review] [spec-name]
description: Manage specifications in our development process
---
## Context
You are managing specifications using our specification-driven development process defined in @docs/specs/SPEC-001.md.
Available commands:
- `create [name]` - Create new specification
- `status` - Show all spec statuses
- `implement [spec-name]` - Hand spec to appropriate agent
- `review [spec-name]` - Review implementation against spec
## Your task
Execute the spec command: `/spec $ARGUMENTS`
### If command is "create":
1. Get next SPEC number by searching existing specs
2. Create new spec using template from @docs/specs/Slash\ Commands\ Reference.md
3. Place in `/specs` folder with title "SPEC-XXX: [name]"
4. Include standard sections: Why, What, How, How to Evaluate
### If command is "status":
1. Search all notes in `/specs` folder
2. Display table with spec number, title, and status
3. Show any dependencies or assigned agents
### If command is "implement":
1. Read the specified spec
2. Determine appropriate agent based on content:
- Frontend/UI → vue-developer
- Architecture/system → system-architect
- Backend/API → python-developer
3. Launch Task tool with appropriate agent and spec context
### If command is "review":
1. Read the specified spec and its "How to Evaluate" section
2. Review current implementation against success criteria with careful evaluation of:
- **Functional completeness** - All specified features working
- **Test coverage analysis** - Actual test files and coverage percentage
- Count existing test files vs required components/APIs/composables
- Verify unit tests, integration tests, and end-to-end tests
- Check for missing test categories (component, API, workflow)
- **Code quality metrics** - TypeScript compilation, linting, performance
- **Architecture compliance** - Component isolation, state management patterns
- **Documentation completeness** - Implementation matches specification
3. Provide honest, accurate assessment - do not overstate completeness
4. Document findings and update spec with review results
5. If gaps found, clearly identify what still needs to be implemented/tested
Use the agent definitions from @docs/specs/Agent\ Definitions.md for implementation handoffs.
+622
View File
@@ -0,0 +1,622 @@
# /project:test-live - Live Basic Memory Testing Suite
Execute comprehensive real-world testing of Basic Memory using the installed version.
All test results are recorded as notes in a dedicated test project.
## Usage
```
/project:test-live [phase]
```
**Parameters:**
- `phase` (optional): Specific test phase to run (`recent`, `core`, `features`, `edge`, `workflows`, `stress`, or `all`)
- `recent` - Focus on recent changes and new features (recommended for regular testing)
- `core` - Essential tools only (Tier 1: write_note, read_note, search_notes, edit_note, list_memory_projects, recent_activity)
- `features` - Core + important workflows (Tier 1 + Tier 2)
- `all` - Comprehensive testing of all tools and scenarios
## Implementation
You are an expert QA engineer conducting live testing of Basic Memory.
When the user runs `/project:test-live`, execute comprehensive test plan:
## Tool Testing Priority
### **Tier 1: Critical Core (Always Test)**
1. **write_note** - Foundation of all knowledge creation
2. **read_note** - Primary knowledge retrieval mechanism
3. **search_notes** - Essential for finding information
4. **edit_note** - Core content modification capability
5. **list_memory_projects** - Project discovery and session guidance
6. **recent_activity** - Project discovery mode and activity analysis
### **Tier 2: Important Workflows (Usually Test)**
7. **build_context** - Conversation continuity via memory:// URLs
8. **create_memory_project** - Essential for project setup
9. **move_note** - Knowledge organization
10. **sync_status** - Understanding system state
11. **delete_project** - Project lifecycle management
### **Tier 3: Enhanced Functionality (Sometimes Test)**
12. **view_note** - Claude Desktop artifact display
13. **read_content** - Raw content access
14. **delete_note** - Content removal
15. **list_directory** - File system exploration
16. **edit_note** (advanced modes) - Complex find/replace operations
### **Tier 4: Specialized (Rarely Test)**
17. **canvas** - Obsidian visualization (specialized use case)
18. **MCP Prompts** - Enhanced UX tools (ai_assistant_guide, continue_conversation)
## Stateless Architecture Testing
### **Project Discovery Workflow (CRITICAL)**
Test the new stateless project selection flow:
1. **Initial Discovery**
- Call `list_memory_projects()` without knowing which project to use
- Verify clear session guidance appears: "Next: Ask which project to use"
- Confirm removal of CLI-specific references
2. **Activity-Based Discovery**
- Call `recent_activity()` without project parameter (discovery mode)
- Verify intelligent project suggestions based on activity
- Test guidance: "Should I use [most-active-project] for this task?"
3. **Session Tracking Validation**
- Verify all tool responses include `[Session: Using project 'name']`
- Confirm guidance reminds about session-wide project tracking
4. **Single Project Constraint Mode**
- Test MCP server with `--project` parameter
- Verify all operations constrained to specified project
- Test project override behavior in constrained mode
### **Explicit Project Parameters (CRITICAL)**
All tools must require explicit project parameters:
1. **Parameter Validation**
- Test all Tier 1 tools require `project` parameter
- Verify clear error messages for missing project
- Test invalid project name handling
2. **No Session State Dependencies**
- Confirm no tool relies on "current project" concept
- Test rapid project switching within conversation
- Verify each call is truly independent
### Pre-Test Setup
1. **Environment Verification**
- Verify basic-memory is installed and accessible via MCP
- Check version and confirm it's the expected release
- Test MCP connection and tool availability
2. **Recent Changes Analysis** (if phase includes 'recent' or 'all')
- Run `git log --oneline -20` to examine recent commits
- Identify new features, bug fixes, and enhancements
- Generate targeted test scenarios for recent changes
- Prioritize regression testing for recently fixed issues
3. **Test Project Creation**
Run the bash `date` command to get the current date/time.
```
Create project: "basic-memory-testing-[timestamp]"
Location: ~/basic-memory-testing-[timestamp]
Purpose: Record all test observations and results
```
Make sure to use the newly created project for all subsequent test operations by specifying it in the `project` parameter of each tool call.
4. **Baseline Documentation**
Create initial test session note with:
- Test environment details
- Version being tested
- Recent changes identified (if applicable)
- Test objectives and scope
- Start timestamp
### Phase 0: Recent Changes Validation (if 'recent' or 'all' phase)
Based on recent commit analysis, create targeted test scenarios:
**Recent Changes Test Protocol:**
1. **Feature Addition Tests** - For each new feature identified:
- Test basic functionality
- Test integration with existing tools
- Verify documentation accuracy
- Test edge cases and error handling
2. **Bug Fix Regression Tests** - For each recent fix:
- Recreate the original problem scenario
- Verify the fix works as expected
- Test related functionality isn't broken
- Document the verification in test notes
3. **Performance/Enhancement Validation** - For optimizations:
- Establish baseline timing
- Compare with expected improvements
- Test under various load conditions
- Document performance observations
**Example Recent Changes (Update based on actual git log):**
- Watch Service Restart (#156): Test project creation → file modification → automatic restart
- Cross-Project Moves (#161): Test move_note with cross-project detection
- Docker Environment Support (#174): Test BASIC_MEMORY_HOME behavior
- MCP Server Logging (#164): Verify log level configurations
### Phase 1: Core Functionality Validation (Tier 1 Tools)
Test essential MCP tools that form the foundation of Basic Memory:
**1. write_note Tests (Critical):**
- ✅ Basic note creation with frontmatter
- ✅ Special characters and Unicode in titles
- ✅ Various content types (lists, headings, code blocks)
- ✅ Empty notes and minimal content edge cases
- ⚠️ Error handling for invalid parameters
**2. read_note Tests (Critical):**
- ✅ Read by title, permalink, memory:// URLs
- ✅ Non-existent notes (error handling)
- ✅ Notes with complex markdown formatting
- ⚠️ Performance with large notes (>10MB)
**3. search_notes Tests (Critical):**
- ✅ Simple text queries across content
- ✅ Tag-based searches with multiple tags
- ✅ Boolean operators (AND, OR, NOT)
- ✅ Empty/no results scenarios
- ⚠️ Performance with 100+ notes
**4. edit_note Tests (Critical):**
- ✅ Append operations preserving frontmatter
- ✅ Prepend operations
- ✅ Find/replace with validation
- ✅ Section replacement under headers
- ⚠️ Error scenarios (invalid operations)
**5. list_memory_projects Tests (Critical):**
- ✅ Display all projects with clear session guidance
- ✅ Project discovery workflow prompts
- ✅ Removal of CLI-specific references
- ✅ Empty project list handling
- ✅ Single project constraint mode display
**6. recent_activity Tests (Critical - Discovery Mode):**
- ✅ Discovery mode without project parameter
- ✅ Intelligent project suggestions based on activity
- ✅ Guidance prompts for project selection
- ✅ Session tracking reminders in responses
- ⚠️ Performance with multiple projects
### Phase 2: Important Workflows (Tier 2 Tools)
**7. build_context Tests (Important):**
- ✅ Different depth levels (1, 2, 3+)
- ✅ Various timeframes for context
- ✅ memory:// URL navigation
- ⚠️ Performance with complex relation graphs
**8. create_memory_project Tests (Important):**
- ✅ Create projects dynamically
- ✅ Set default during creation
- ✅ Path validation and creation
- ⚠️ Invalid paths and names
- ✅ Integration with existing projects
**9. move_note Tests (Important):**
- ✅ Move within same project
- ✅ Cross-project moves with detection (#161)
- ✅ Automatic folder creation
- ✅ Database consistency validation
- ⚠️ Special characters in paths
**10. sync_status Tests (Important):**
- ✅ Background operation monitoring
- ✅ File synchronization status
- ✅ Project sync state reporting
- ⚠️ Error state handling
### Phase 3: Enhanced Functionality (Tier 3 Tools)
**11. view_note Tests (Enhanced):**
- ✅ Claude Desktop artifact display
- ✅ Title extraction from frontmatter
- ✅ Unicode and emoji content rendering
- ⚠️ Error handling for non-existent notes
**12. read_content Tests (Enhanced):**
- ✅ Raw file content access
- ✅ Binary file handling
- ✅ Image file reading
- ⚠️ Large file performance
**13. delete_note Tests (Enhanced):**
- ✅ Single note deletion
- ✅ Database consistency after deletion
- ⚠️ Non-existent note handling
- ✅ Confirmation of successful deletion
**14. list_directory Tests (Enhanced):**
- ✅ Directory content listing
- ✅ Depth control and filtering
- ✅ File name globbing
- ⚠️ Empty directory handling
**15. delete_project Tests (Enhanced):**
- ✅ Project removal from config
- ✅ Database cleanup
- ⚠️ Default project protection
- ⚠️ Non-existent project handling
### Phase 4: Edge Case Exploration
**Boundary Testing:**
- Very long titles and content (stress limits)
- Empty projects and notes
- Unicode, emojis, special symbols
- Deeply nested folder structures
- Circular relations and self-references
- Maximum relation depths
**Error Scenarios:**
- Invalid memory:// URLs
- Missing files referenced in database
- Invalid project names and paths
- Malformed note structures
- Concurrent operation conflicts
**Performance Testing:**
- Create 100+ notes rapidly
- Complex search queries
- Deep relation chains (5+ levels)
- Rapid successive operations
- Memory usage monitoring
### Phase 5: Real-World Workflow Scenarios
**Meeting Notes Pipeline:**
1. Create meeting notes with action items
2. Extract action items using edit_note
3. Build relations to project documents
4. Update progress incrementally
5. Search and track completion
**Research Knowledge Building:**
1. Create research topic hierarchy
2. Build complex relation networks
3. Add incremental findings over time
4. Search for connections and patterns
5. Reorganize as knowledge evolves
**Multi-Project Workflow:**
1. Technical documentation project
2. Personal recipe collection project
3. Learning/course notes project
4. Specify different projects for different operations
5. Cross-reference related concepts
**Content Evolution:**
1. Start with basic notes
2. Enhance with relations and observations
3. Reorganize file structure using moves
4. Update content with edit operations
5. Validate knowledge graph integrity
### Phase 6: Specialized Tools Testing (Tier 4)
**16. canvas Tests (Specialized):**
- ✅ JSON Canvas generation
- ✅ Node and edge creation
- ✅ Obsidian compatibility
- ⚠️ Complex graph handling
**17. MCP Prompts Tests (Specialized):**
- ✅ ai_assistant_guide output
- ✅ continue_conversation functionality
- ✅ Formatted search results
- ✅ Enhanced activity reports
### Phase 7: Integration & File Watching Tests
**File System Integration:**
- ✅ Watch service behavior with file changes
- ✅ Project creation → watch restart (#156)
- ✅ Multi-project synchronization
- ⚠️ MCP→API→DB→File stack validation
**Real Integration Testing:**
- ✅ End-to-end file watching vs manual operations
- ✅ Cross-session persistence
- ✅ Database consistency across operations
- ⚠️ Performance under real file system changes
### Phase 8: Creative Stress Testing
**Creative Exploration:**
- Rapid project creation/switching patterns
- Unusual but valid markdown structures
- Creative observation categories
- Novel relation types and patterns
- Unexpected tool combinations
**Stress Scenarios:**
- Bulk operations (many notes quickly)
- Complex nested moves and edits
- Deep context building
- Complex boolean search expressions
- Resource constraint testing
## Test Execution Guidelines
### Quick Testing (core/features phases)
- Focus on Tier 1 tools (core) or Tier 1+2 (features)
- Test essential functionality and common edge cases
- Record critical issues immediately
- Complete in 15-20 minutes
### Comprehensive Testing (all phase)
- Cover all tiers systematically
- Include specialized tools and stress testing
- Document performance baselines
- Complete in 45-60 minutes
### Recent Changes Focus (recent phase)
- Analyze git log for recent commits
- Generate targeted test scenarios
- Focus on regression testing for fixes
- Validate new features thoroughly
## Test Observation Format
Record ALL observations immediately as Basic Memory notes:
```markdown
---
title: Test Session [Phase] YYYY-MM-DD HH:MM
tags: [testing, v0.13.0, live-testing, [phase]]
permalink: test-session-[phase]-[timestamp]
---
# Test Session [Phase] - [Date/Time]
## Environment
- Basic Memory version: [version]
- MCP connection: [status]
- Test project: [name]
- Phase focus: [description]
## Test Results
### ✅ Successful Operations
- [timestamp] ✅ write_note: Created note with emoji title 📝 #tier1 #functionality
- [timestamp] ✅ search_notes: Boolean query returned 23 results in 0.4s #tier1 #performance
- [timestamp] ✅ edit_note: Append operation preserved frontmatter #tier1 #reliability
### ⚠️ Issues Discovered
- [timestamp] ⚠️ move_note: Slow with deep folder paths (2.1s) #tier2 #performance
- [timestamp] 🚨 search_notes: Unicode query returned unexpected results #tier1 #bug #critical
- [timestamp] ⚠️ build_context: Context lost for memory:// URLs #tier2 #issue
### 🚀 Enhancements Identified
- edit_note could benefit from preview mode #ux-improvement
- search_notes needs fuzzy matching for typos #feature-idea
- move_note could auto-suggest folder creation #usability
### 📊 Performance Metrics
- Average write_note time: 0.3s
- Search with 100+ notes: 0.6s
- Project parameter overhead: <0.1s
- Memory usage: [observed levels]
## Relations
- tests [[Basic Memory v0.13.0]]
- part_of [[Live Testing Suite]]
- found_issues [[Bug Report: Unicode Search]]
- discovered [[Performance Optimization Opportunities]]
```
## Quality Assessment Areas
**User Experience & Usability:**
- Tool instruction clarity and examples
- Error message actionability
- Response time acceptability
- Tool consistency and discoverability
- Learning curve and intuitiveness
**System Behavior:**
- Stateless operation independence
- memory:// URL navigation reliability
- Multi-step workflow cohesion
- Edge case graceful handling
- Recovery from user errors
**Documentation Alignment:**
- Tool output clarity and helpfulness
- Behavior vs. documentation accuracy
- Example validity and usefulness
- Real-world vs. documented workflows
**Mental Model Validation:**
- Natural user expectation alignment
- Surprising behavior identification
- Mistake recovery ease
- Knowledge graph concept naturalness
**Performance & Reliability:**
- Operation completion times
- Consistency across sessions
- Scaling behavior with growth
- Unexpected slowness identification
## Error Documentation Protocol
For each error discovered:
1. **Immediate Recording**
- Create dedicated error note
- Include exact reproduction steps
- Capture error messages verbatim
- Note system state when error occurred
2. **Error Note Format**
```markdown
---
title: Bug Report - [Short Description]
tags: [bug, testing, v0.13.0, [severity]]
---
# Bug Report: [Description]
## Reproduction Steps
1. [Exact steps to reproduce]
2. [Include all parameters used]
3. [Note any special conditions]
## Expected Behavior
[What should have happened]
## Actual Behavior
[What actually happened]
## Error Messages
```
[Exact error text]
```
## Environment
- Version: [version]
- Project: [name]
- Timestamp: [when]
## Severity
- [ ] Critical (blocks major functionality)
- [ ] High (impacts user experience)
- [ ] Medium (workaround available)
- [ ] Low (minor inconvenience)
## Relations
- discovered_during [[Test Session [Phase]]]
- affects [[Feature Name]]
```
## Success Metrics Tracking
**Quantitative Measures:**
- Test scenario completion rate
- Bug discovery count with severity
- Performance benchmark establishment
- Tool coverage completeness
**Qualitative Measures:**
- Conversation flow naturalness
- Knowledge graph quality
- User experience insights
- System reliability assessment
## Test Execution Flow
1. **Setup Phase** (5 minutes)
- Verify environment and create test project
- Record baseline system state
- Establish performance benchmarks
2. **Core Testing** (15-20 minutes per phase)
- Execute test scenarios systematically
- Record observations immediately
- Note timestamps for performance tracking
- Explore variations when interesting behaviors occur
3. **Documentation** (5 minutes per phase)
- Create phase summary note
- Link related test observations
- Update running issues list
- Record enhancement ideas
4. **Analysis Phase** (10 minutes)
- Review all observations across phases
- Identify patterns and trends
- Create comprehensive summary report
- Generate development recommendations
## Testing Success Criteria
### Core Testing (Tier 1) - Must Pass
- All 6 critical tools function correctly
- No critical bugs in essential workflows
- Acceptable performance for basic operations
- Error handling works as expected
### Feature Testing (Tier 1+2) - Should Pass
- All 11 core + important tools function
- Workflow scenarios complete successfully
- Performance meets baseline expectations
- Integration points work correctly
### Comprehensive Testing (All Tiers) - Complete Coverage
- All tools tested across all scenarios
- Edge cases and stress testing completed
- Performance baselines established
- Full documentation of issues and enhancements
## Expected Outcomes
**System Validation:**
- Feature verification prioritized by tier importance
- Recent changes validated for regression
- Performance baseline establishment
- Bug identification with severity assessment
**Knowledge Base Creation:**
- Prioritized testing documentation
- Real usage examples for user guides
- Recent changes validation records
- Performance insights for optimization
**Development Insights:**
- Tier-based bug priority list
- Recent changes impact assessment
- Enhancement ideas from real usage
- User experience improvement areas
## Post-Test Deliverables
1. **Test Summary Note**
- Overall results and findings
- Critical issues requiring immediate attention
- Enhancement opportunities discovered
- System readiness assessment
2. **Bug Report Collection**
- All discovered issues with reproduction steps
- Severity and impact assessments
- Suggested fixes where applicable
3. **Performance Baseline**
- Timing data for all operations
- Scaling behavior observations
- Resource usage patterns
4. **UX Improvement Recommendations**
- Usability enhancement suggestions
- Documentation improvement areas
- Tool design optimization ideas
5. **Updated TESTING.md**
- Incorporate new test scenarios discovered
- Update based on real execution experience
- Add performance benchmarks and targets
## Context
- Uses real installed basic-memory version
- Tests complete MCP→API→DB→File stack
- Creates living documentation in Basic Memory itself
- Follows integration over isolation philosophy
- Prioritizes testing by tool importance and usage frequency
- Adapts to recent development changes dynamically
- Focuses on real usage patterns over checklist validation
- Generates actionable insights prioritized by impact
+60
View File
@@ -0,0 +1,60 @@
# Git files
.git/
.gitignore
.gitattributes
# Development files
.vscode/
.idea/
*.swp
*.swo
*~
# Testing files
tests/
test-int/
.pytest_cache/
.coverage
htmlcov/
# Build artifacts
build/
dist/
*.egg-info/
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
# Virtual environments (uv creates these during build)
.venv/
venv/
.env
# CI/CD files
.github/
# Documentation (keep README.md and pyproject.toml)
docs/
CHANGELOG.md
CLAUDE.md
CONTRIBUTING.md
# Example files not needed for runtime
examples/
# Local development files
.basic-memory/
*.db
*.sqlite3
# OS files
.DS_Store
Thumbs.db
# Temporary files
tmp/
temp/
*.tmp
*.log
+79
View File
@@ -0,0 +1,79 @@
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
jobs:
claude-review:
# Only run for organization members and collaborators
if: |
github.event.pull_request.author_association == 'OWNER' ||
github.event.pull_request.author_association == 'MEMBER' ||
github.event.pull_request.author_association == 'COLLABORATOR'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
track_progress: true # Enable visual progress tracking
allowed_bots: '*'
prompt: |
Review this Basic Memory PR against our team checklist:
## Code Quality & Standards
- [ ] Follows Basic Memory's coding conventions in CLAUDE.md
- [ ] Python 3.12+ type annotations and async patterns
- [ ] SQLAlchemy 2.0 best practices
- [ ] FastAPI and Typer conventions followed
- [ ] 100-character line length limit maintained
- [ ] No commented-out code blocks
## Testing & Documentation
- [ ] Unit tests for new functions/methods
- [ ] Integration tests for new MCP tools
- [ ] Test coverage for edge cases
- [ ] Documentation updated (README, docstrings)
- [ ] CLAUDE.md updated if conventions change
## Basic Memory Architecture
- [ ] MCP tools follow atomic, composable design
- [ ] Database changes include Alembic migrations
- [ ] Preserves local-first architecture principles
- [ ] Knowledge graph operations maintain consistency
- [ ] Markdown file handling preserves integrity
- [ ] AI-human collaboration patterns followed
## Security & Performance
- [ ] No hardcoded secrets or credentials
- [ ] Input validation for MCP tools
- [ ] Proper error handling and logging
- [ ] Performance considerations addressed
- [ ] No sensitive data in logs or commits
Read the CLAUDE.md file for detailed project context. For each checklist item, verify if it's satisfied and comment on any that need attention. Use inline comments for specific code issues and post a summary with checklist results.
# Allow broader tool access for thorough code review
claude_args: '--allowed-tools "Bash(gh pr:*),Bash(gh issue:*),Bash(gh api:*),Bash(git log:*),Bash(git show:*),Read,Grep,Glob"'
+71
View File
@@ -0,0 +1,71 @@
name: Claude Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
permissions:
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Issue Triage
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true # Show triage progress
prompt: |
Analyze this new Basic Memory issue and perform triage:
**Issue Analysis:**
1. **Type Classification:**
- Bug report (code defect)
- Feature request (new functionality)
- Enhancement (improvement to existing feature)
- Documentation (docs improvement)
- Question/Support (user help)
- MCP tool issue (specific to MCP functionality)
2. **Priority Assessment:**
- Critical: Security issues, data loss, complete breakage
- High: Major functionality broken, affects many users
- Medium: Minor bugs, usability issues
- Low: Nice-to-have improvements, cosmetic issues
3. **Component Classification:**
- CLI commands
- MCP tools
- Database/sync
- Cloud functionality
- Documentation
- Testing
4. **Complexity Estimate:**
- Simple: Quick fix, documentation update
- Medium: Requires some investigation/testing
- Complex: Major feature work, architectural changes
**Actions to Take:**
1. Add appropriate labels using: `gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2"`
2. Check for duplicates using: `gh search issues`
3. If duplicate found, comment mentioning the original issue
4. For feature requests, ask clarifying questions if needed
5. For bugs, request reproduction steps if missing
**Available Labels:**
- Type: bug, enhancement, feature, documentation, question, mcp-tool
- Priority: critical, high, medium, low
- Component: cli, mcp, database, cloud, docs, testing
- Complexity: simple, medium, complex
- Status: needs-reproduction, needs-clarification, duplicate
Read the issue carefully and provide helpful triage with appropriate labels.
claude_args: '--allowed-tools "Bash(gh issue:*),Bash(gh search:*),Read"'
+68
View File
@@ -0,0 +1,68 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
pull_request_target:
types: [opened, synchronize]
jobs:
claude:
if: |
(
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@claude'))
) && (
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR' ||
github.event.sender.author_association == 'OWNER' ||
github.event.sender.author_association == 'MEMBER' ||
github.event.sender.author_association == 'COLLABORATOR' ||
github.event.pull_request.author_association == 'OWNER' ||
github.event.pull_request.author_association == 'MEMBER' ||
github.event.pull_request.author_association == 'COLLABORATOR'
)
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# For pull_request_target, checkout the PR head to review the actual changes
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
fetch-depth: 1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true # Enable visual progress tracking
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'
# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.claude.com/en/docs/claude-code/sdk#command-line for available options
# claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)'
+53
View File
@@ -0,0 +1,53 @@
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
+61
View File
@@ -0,0 +1,61 @@
name: Docker Image CI
on:
push:
tags:
- 'v*' # Trigger on version tags like v1.0.0, v0.13.0, etc.
workflow_dispatch: # Allow manual triggering for testing
env:
REGISTRY: ghcr.io
IMAGE_NAME: basicmachines-co/basic-memory
jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+54 -65
View File
@@ -1,96 +1,85 @@
name: Release
on:
workflow_dispatch:
inputs:
version_type:
description: 'Type of version bump (major, minor, patch)'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
push:
tags:
- 'v*' # Trigger on version tags like v1.0.0, v0.13.0, etc.
jobs:
release:
runs-on: ubuntu-latest
concurrency: release
permissions:
id-token: write
contents: write
outputs:
released: ${{ steps.release.outputs.released }}
tag: ${{ steps.release.outputs.tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Python Semantic Release
id: release
uses: python-semantic-release/python-semantic-release@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: steps.release.outputs.released == 'true'
with:
password: ${{ secrets.PYPI_TOKEN }}
- name: Publish to GitHub Release Assets
uses: python-semantic-release/publish-action@v9.8.9
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}
build-macos:
needs: release
if: needs.release.outputs.released == 'true'
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.tag }}
- name: Set up Python "3.12"
uses: actions/setup-python@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: 'pip'
- name: Install librsvg
run: brew install librsvg
- name: Install uv
run: |
pip install uv
- name: Create virtual env
- name: Install dependencies and build
run: |
uv venv
- name: Install dependencies
run: |
uv sync
uv build
- name: Build macOS installer
- name: Verify build succeeded
run: |
make installer-mac
xattr -dr com.apple.quarantine "installer/build/Basic Memory Installer.app"
# Verify that build artifacts exist
ls -la dist/
echo "Build completed successfully"
- name: Zip macOS installer
run: |
cd installer/build
zip -ry "Basic-Memory-Installer-${{ needs.release.outputs.tag }}.zip" "Basic Memory Installer.app"
- name: Upload macOS installer
uses: softprops/action-gh-release@v1
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: installer/build/Basic-Memory-Installer-${{ needs.release.outputs.tag }}.zip
tag_name: ${{ needs.release.outputs.tag }}
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 }}
homebrew:
name: Update Homebrew Formula
needs: release
runs-on: ubuntu-latest
# Only run for stable releases (not dev, beta, or rc versions)
if: ${{ !contains(github.ref_name, 'dev') && !contains(github.ref_name, 'b') && !contains(github.ref_name, 'rc') }}
permissions:
contents: write
actions: read
steps:
- name: Update Homebrew formula
uses: mislav/bump-homebrew-formula-action@v3
with:
# Formula name in homebrew-basic-memory repo
formula-name: basic-memory
# The tap repository
homebrew-tap: basicmachines-co/homebrew-basic-memory
# Base branch of the tap repository
base-branch: main
# Download URL will be automatically constructed from the tag
download-url: https://github.com/basicmachines-co/basic-memory/archive/refs/tags/${{ github.ref_name }}.tar.gz
# Commit message for the formula update
commit-message: |
{{formulaName}} {{version}}
Created by https://github.com/basicmachines-co/basic-memory/actions/runs/${{ github.run_id }}
env:
# Personal Access Token with repo scope for homebrew-basic-memory repo
COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TOKEN }}
+31 -4
View File
@@ -5,14 +5,21 @@ on:
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" ]
os: [ubuntu-latest, windows-latest]
python-version: [ "3.12", "3.13" ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -29,6 +36,18 @@ jobs:
run: |
pip install uv
- name: Install just (Linux/macOS)
if: runner.os != 'Windows'
run: |
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
- name: Install just (Windows)
if: runner.os == 'Windows'
run: |
# Install just using Chocolatey (pre-installed on GitHub Actions Windows runners)
choco install just --yes
shell: pwsh
- name: Create virtual env
run: |
uv venv
@@ -39,9 +58,17 @@ jobs:
- name: Run type checks
run: |
uv run make type-check
just typecheck
- name: Run type checks
run: |
just typecheck
- name: Run linting
run: |
just lint
- name: Run tests
run: |
uv pip install pytest pytest-cov
uv run make test
just test
+7 -2
View File
@@ -42,9 +42,14 @@ ENV/
# macOS
.DS_Store
/.coverage.*
.coverage.*
# obsidian docs:
/docs/.obsidian/
/examples/.obsidian/
/examples/.basic-memory/
/examples/.basic-memory/
# claude action
claude-output
**/.claude/settings.local.json
+1032 -1
View File
File diff suppressed because it is too large Load Diff
+63 -26
View File
@@ -1,34 +1,71 @@
Developer Certificate of Origin
Version 1.1
https://developercertificate.org/
# Contributor License Agreement
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
## Copyright Assignment and License Grant
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
By signing this Contributor License Agreement ("Agreement"), you accept and agree to the following terms and conditions
for your present and future Contributions submitted
to Basic Machines LLC. Except for the license granted herein to Basic Machines LLC and recipients of software
distributed by Basic Machines LLC, you reserve all right,
title, and interest in and to your Contributions.
Developer's Certificate of Origin 1.1
### 1. Definitions
By making a contribution to this project, I certify that:
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this
Agreement with Basic Machines LLC.
(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
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work,
that is intentionally submitted by You to Basic
Machines LLC for inclusion in, or documentation of, any of the products owned or managed by Basic Machines LLC (the "
Work").
(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
### 2. Grant of Copyright License
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
software distributed by Basic Machines LLC a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Work, and to permit persons to whom the Work is furnished to do so.
(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.
### 3. Assignment of Copyright
You hereby assign to Basic Machines LLC all right, title, and interest worldwide in all Copyright covering your
Contributions. Basic Machines LLC may license the
Contributions under any license terms, including copyleft, permissive, commercial, or proprietary licenses.
### 4. Grant of Patent License
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
software distributed by Basic Machines LLC a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to
make, have made, use, offer to sell, sell, import, and
otherwise transfer the Work.
### 5. Developer Certificate of Origin
By making a Contribution to this project, You certify that:
(a) The Contribution was created in whole or in part by You and You have the right to submit it under this Agreement; or
(b) The Contribution is based upon previous work that, to the best of Your knowledge, is covered under an appropriate
open source license and You have the right under that
license to submit that work with modifications, whether created in whole or in part by You, under this Agreement; or
(c) The Contribution was provided directly to You by some other person who certified (a), (b) or (c) and You have not
modified it.
(d) You understand and agree that this project and the Contribution are public and that a record of the Contribution (
including all personal information You submit with
it, including Your sign-off) is maintained indefinitely and may be redistributed consistent with this project or the
open source license(s) involved.
### 6. Representations
You represent that you are legally entitled to grant the above license and assignment. If your employer(s) has rights to
intellectual property that you create that
includes your Contributions, you represent that you have received permission to make Contributions on behalf of that
employer, or that your employer has waived such rights
for your Contributions to Basic Machines LLC.
---
This Agreement is effective as of the date you first submit a Contribution to Basic Machines LLC.
+1 -1
View File
@@ -63,7 +63,7 @@ See the [README.md](README.md) file for a project overview.
- Testing uses pytest with asyncio support (strict mode)
- Test database uses in-memory SQLite
- Avoid creating mocks in tests in most circumstances.
- Each test runs in a standalone enviroment with in memory SQLite and tmp_file directory
- Each test runs in a standalone environment with in memory SQLite and tmp_file directory
## BASIC MEMORY PRODUCT USAGE
+50 -9
View File
@@ -15,8 +15,8 @@ project and how to get started as a developer.
2. **Install Dependencies**:
```bash
# Using make (recommended)
make install
# Using just (recommended)
just install
# Or using uv
uv install -e ".[dev]"
@@ -25,10 +25,17 @@ project and how to get started as a developer.
pip install -e ".[dev]"
```
3. **Run the Tests**:
> **Note**: Basic Memory uses [just](https://just.systems) as a modern command runner. Install with `brew install just` or `cargo install just`.
3. **Activate the Virtual Environment**
```bash
source .venv/bin/activate
```
4. **Run the Tests**:
```bash
# Run all tests
make test
just test
# or
uv run pytest -p pytest_mock -v
@@ -49,16 +56,16 @@ project and how to get started as a developer.
4. **Check Code Quality**:
```bash
# Run all checks at once
make check
just check
# Or run individual checks
make lint # Run linting
make format # Format code
make type-check # Type checking
just lint # Run linting
just format # Format code
just type-check # Type checking
```
5. **Test Your Changes**: Ensure all tests pass locally and maintain 100% test coverage.
```bash
make test
just test
```
6. **Submit a PR**: Submit a pull request with a detailed description of your changes.
@@ -144,6 +151,40 @@ agreement to the DCO.
- **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:
+39 -10
View File
@@ -1,16 +1,45 @@
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM python:3.12-slim
FROM python:3.12-slim-bookworm
# Build arguments for user ID and group ID (defaults to 1000)
ARG UID=1000
ARG GID=1000
# Copy uv from official image
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Set environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# Create a group and user with the provided UID/GID
# Check if the GID already exists, if not create appgroup
RUN (getent group ${GID} || groupadd --gid ${GID} appgroup) && \
useradd --uid ${UID} --gid ${GID} --create-home --shell /bin/bash appuser
# Copy the project into the image
ADD . /app
# Sync the project into a new environment, asserting the lockfile is up to date
WORKDIR /app
RUN uv sync --locked
# Copy the project files
COPY . .
# Create necessary directories and set ownership
RUN mkdir -p /app/data /app/.basic-memory && \
chown -R appuser:${GID} /app
# Install pip and build dependencies
RUN pip install --upgrade pip \
&& pip install . --no-cache-dir --ignore-installed
# Set default data directory and add venv to PATH
ENV BASIC_MEMORY_HOME=/app/data \
PATH="/app/.venv/bin:$PATH"
# Expose port if necessary (e.g., uv might use a port, but MCP over stdio so not needed here)
# Switch to the non-root user
USER appuser
# Use the basic-memory entrypoint to run the MCP server
CMD ["basic-memory", "mcp"]
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD basic-memory --version || exit 1
# Use the basic-memory entrypoint to run the MCP server with default SSE transport
CMD ["basic-memory", "mcp", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]
-67
View File
@@ -1,67 +0,0 @@
.PHONY: install test test-module lint clean format type-check installer-mac installer-win check
install:
pip install -e ".[dev]"
test:
uv run pytest -p pytest_mock -v
# Run tests for a specific module
# Usage: make test-module m=path/to/module.py [cov=module_path]
test-module:
@if [ -z "$(m)" ]; then \
echo "Usage: make test-module m=path/to/module.py [cov=module_path]"; \
exit 1; \
fi; \
if [ -z "$(cov)" ]; then \
uv run pytest $(m) -v; \
else \
uv run pytest $(m) -v --cov=$(cov); \
fi
lint:
ruff check . --fix
type-check:
uv run pyright
clean:
find . -type f -name '*.pyc' -delete
find . -type d -name '__pycache__' -exec rm -r {} +
rm -rf installer/build/
rm -rf installer/dist/
rm -f rw.*.dmg
rm -rf dist
rm -rf installer/build
rm -rf installer/dist
rm -f .coverage.*
format:
uv run ruff format .
# run inspector tool
run-inspector:
uv run mcp dev src/basic_memory/mcp/main.py
# Build app installer
installer-mac:
cd installer && chmod +x make_icons.sh && ./make_icons.sh
cd installer && uv run python setup.py bdist_mac
installer-win:
cd installer && uv run python setup.py bdist_win32
update-deps:
uv lock --upgrade
check: lint format type-check test
# Target for generating Alembic migrations with a message from command line
migration:
@if [ -z "$(m)" ]; then \
echo "Usage: make migration m=\"Your migration message\""; \
exit 1; \
fi; \
cd src/basic_memory/alembic && alembic revision --autogenerate -m "$(m)"
+50 -6
View File
@@ -3,6 +3,8 @@
[![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
@@ -11,8 +13,8 @@ Basic Memory lets you build persistent knowledge through natural conversations w
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: http://basicmachines.co
- Documentation: http://memory.basicmachines.co
- Website: https://basicmachines.co
- Documentation: https://memory.basicmachines.co
## Pick up your conversation right where you left off
@@ -260,6 +262,38 @@ Examples of relations:
- documented_in [[Coffee Journal]]
```
## Using with VS Code
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"]
}
}
}
```
You can use Basic Memory with VS Code to easily retrieve and store information while coding.
## Using with Claude Desktop
Basic Memory is built using the MCP (Model Context Protocol) and works with the Claude desktop app (https://claude.ai/):
@@ -337,10 +371,10 @@ canvas(nodes, edges, title, folder) - Generate knowledge visualizations
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)
- [Complete User Guide](https://docs.basicmemory.com/user-guide/)
- [CLI tools](https://docs.basicmemory.com/guides/cli-reference/)
- [Managing multiple Projects](https://docs.basicmemory.com/guides/cli-reference/#project)
- [Importing data from OpenAI/Claude Projects](https://docs.basicmemory.com/guides/cli-reference/#import)
## License
@@ -349,4 +383,14 @@ 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
-1419
View File
File diff suppressed because it is too large Load Diff
+83
View File
@@ -0,0 +1,83 @@
# Docker Compose configuration for Basic Memory
# See docs/Docker.md for detailed setup instructions
version: '3.8'
services:
basic-memory:
# Use pre-built image (recommended for most users)
image: ghcr.io/basicmachines-co/basic-memory:latest
# Uncomment to build locally instead:
# build: .
container_name: basic-memory-server
# Volume mounts for knowledge directories and persistent data
volumes:
# Persistent storage for configuration and database
- basic-memory-config:/root/.basic-memory:rw
# Mount your knowledge directory (required)
# Change './knowledge' to your actual Obsidian vault or knowledge directory
- ./knowledge:/app/data:rw
# OPTIONAL: Mount additional knowledge directories for multiple projects
# - ./work-notes:/app/data/work:rw
# - ./personal-notes:/app/data/personal:rw
# You can edit the project config manually in the mounted config volume
# The default project will be configured to use /app/data
environment:
# Project configuration
- BASIC_MEMORY_DEFAULT_PROJECT=main
# Enable real-time file synchronization (recommended for Docker)
- BASIC_MEMORY_SYNC_CHANGES=true
# Logging configuration
- BASIC_MEMORY_LOG_LEVEL=INFO
# Sync delay in milliseconds (adjust for performance vs responsiveness)
- BASIC_MEMORY_SYNC_DELAY=1000
# Port exposure for HTTP transport (only needed if not using STDIO)
ports:
- "8000:8000"
# Command with SSE transport (configurable via environment variables above)
# IMPORTANT: The SSE and streamable-http endpoints are not secured
command: ["basic-memory", "mcp", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]
# Container management
restart: unless-stopped
# Health monitoring
healthcheck:
test: ["CMD", "basic-memory", "--version"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# Optional: Resource limits
# deploy:
# resources:
# limits:
# memory: 512M
# cpus: '0.5'
# reservations:
# memory: 256M
# cpus: '0.25'
volumes:
# Named volume for persistent configuration and database
# This ensures your configuration and knowledge graph persist across container restarts
basic-memory-config:
driver: local
# Network configuration (optional)
# networks:
# basic-memory-net:
# driver: bridge
-396
View File
@@ -1,396 +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.
# AI Assistant Guide for Basic Memory
This guide helps you, the AI assistant, use Basic Memory tools effectively when working with users. It covers reading, writing, and navigating knowledge through the Model Context Protocol (MCP).
## Overview
Basic Memory allows you and users to record context in local Markdown files, building a rich knowledge base through natural conversations. The system automatically creates a semantic knowledge graph from simple text patterns.
- **Local-First**: All data is stored in plain text files on the user's computer
- **Real-Time**: Users see content updates immediately
- **Bi-Directional**: Both you and users can read and edit notes
- **Semantic**: Simple patterns create a structured knowledge graph
- **Persistent**: Knowledge persists across sessions and conversations
## The Importance of the Knowledge Graph
Basic Memory's value comes from connections between notes, not just the notes themselves. When writing notes, your primary goal should be creating a rich, interconnected knowledge graph.
When creating content, focus on:
1. **Increasing Semantic Density**: Add multiple observations and relations to each note
2. **Using Accurate References**: Aim to reference existing entities by their exact titles
3. **Creating Forward References**: Feel free to reference entities that don't exist yet - Basic Memory will resolve these when they're created later
4. **Creating Bidirectional Links**: When appropriate, connect entities from both directions
5. **Using Meaningful Categories**: Add semantic context with appropriate observation categories
6. **Choosing Precise Relations**: Use specific relation types that convey meaning
Remember that a knowledge graph with 10 heavily connected notes is more valuable than 20 isolated notes. Your job is to help build these connections.
## Core Tools Reference
```python
# Writing knowledge - THE MOST IMPORTANT TOOL!
response = await write_note(
title="Search Design", # Required: Note title
content="# Search Design\n...", # Required: Note content
folder="specs", # Optional: Folder to save in
tags=["search", "design"], # Optional: Tags for categorization
verbose=True # Optional: Get parsing details
)
# Reading knowledge
content = await read_note("Search Design") # By title
content = await read_note("specs/search-design") # By path
content = await read_note("memory://specs/search") # By memory URL
# Searching for knowledge
results = await search(
query="authentication system", # Text to search for
page=1, # Optional: Pagination
page_size=10 # Optional: Results per page
)
# Building context from the knowledge graph
context = await build_context(
url="memory://specs/search", # Starting point
depth=2, # Optional: How many hops to follow
timeframe="1 month" # Optional: Recent timeframe
)
# Checking recent changes
activity = await recent_activity(
type="all", # Optional: Entity types to include
depth=1, # Optional: Related items to include
timeframe="1 week" # Optional: Time window
)
# Creating a knowledge visualization
canvas_result = await canvas(
nodes=[{"id": "note1", "label": "Search Design"}], # Nodes to display
edges=[{"from": "note1", "to": "note2"}], # Connections
title="Project Overview", # Canvas title
folder="diagrams" # Storage location
)
```
## memory:// URLs Explained
Basic Memory uses a special URL format to reference entities in the knowledge graph:
- `memory://title` - Reference by title
- `memory://folder/title` - Reference by folder and title
- `memory://permalink` - Reference by permalink
- `memory://path/relation_type/*` - Follow all relations of a specific type
- `memory://path/*/target` - Find all entities with relations to target
## Semantic Markdown Format
Knowledge is encoded in standard markdown using simple patterns:
**Observations** - Facts about an entity:
```markdown
- [category] This is an observation #tag1 #tag2 (optional context)
```
**Relations** - Links between entities:
```markdown
- relation_type [[Target Entity]] (optional context)
```
**Common Categories & Relation Types:**
- Categories: `[idea]`, `[decision]`, `[question]`, `[fact]`, `[requirement]`, `[technique]`, `[recipe]`, `[preference]`
- Relations: `relates_to`, `implements`, `requires`, `extends`, `part_of`, `pairs_with`, `inspired_by`, `originated_from`
## When to Record Context
**Always consider recording context when**:
1. Users make decisions or reach conclusions
2. Important information emerges during conversation
3. Multiple related topics are discussed
4. The conversation contains information that might be useful later
5. Plans, tasks, or action items are mentioned
**Protocol for recording context**:
1. Identify valuable information in the conversation
2. Ask the user: "Would you like me to record our discussion about [topic] in Basic Memory?"
3. If they agree, use `write_note` to capture the information
4. If they decline, continue without recording
5. Let the user know when information has been recorded: "I've saved our discussion about [topic] to Basic Memory."
## Understanding User Interactions
Users will interact with Basic Memory in patterns like:
1. **Creating knowledge**:
```
Human: "Let's write up what we discussed about search."
You: I'll create a note capturing our discussion about the search functionality.
[Use write_note() to record the conversation details]
```
2. **Referencing existing knowledge**:
```
Human: "Take a look at memory://specs/search"
You: I'll examine that information.
[Use build_context() to gather related information]
[Then read_note() to access specific content]
```
3. **Finding information**:
```
Human: "What were our decisions about auth?"
You: Let me find that information for you.
[Use search() to find relevant notes]
[Then build_context() to understand connections]
```
## Key Things to Remember
1. **Files are Truth**
- All knowledge lives in local files on the user's computer
- Users can edit files outside your interaction
- Changes need to be synced by the user (usually automatic)
- Always verify information is current with `recent_activity()`
2. **Building Context Effectively**
- Start with specific entities
- Follow meaningful relations
- Check recent changes
- Build context incrementally
- Combine related information
3. **Writing Knowledge Wisely**
- Using the same title+folder will overwrite existing notes
- Structure content with clear headings and sections
- Use semantic markup for observations and relations
- Keep files organized in logical folders
## Common Knowledge Patterns
### Capturing Decisions
```markdown
# Coffee Brewing Methods
## Context
I've experimented with various brewing methods including French press, pour over, and espresso.
## Decision
Pour over is my preferred method for light to medium roasts because it highlights subtle flavors and offers more control over the extraction.
## Observations
- [technique] Blooming the coffee grounds for 30 seconds improves extraction #brewing
- [preference] Water temperature between 195-205°F works best #temperature
- [equipment] Gooseneck kettle provides better control of water flow #tools
## Relations
- pairs_with [[Light Roast Beans]]
- contrasts_with [[French Press Method]]
- requires [[Proper Grinding Technique]]
```
### Recording Project Structure
```markdown
# Garden Planning
## Overview
This document outlines the garden layout and planting strategy for this season.
## Observations
- [structure] Raised beds in south corner for sun exposure #layout
- [structure] Drip irrigation system installed for efficiency #watering
- [pattern] Companion planting used to deter pests naturally #technique
## Relations
- contains [[Vegetable Section]]
- contains [[Herb Garden]]
- implements [[Organic Gardening Principles]]
```
### Technical Discussions
```markdown
# Recipe Improvement Discussion
## Key Points
Discussed strategies for improving the chocolate chip cookie recipe.
## Observations
- [issue] Cookies spread too thin when baked at 350°F #texture
- [solution] Chilling dough for 24 hours improves flavor and reduces spreading #technique
- [decision] Will use brown butter instead of regular butter #flavor
## Relations
- improves [[Basic Cookie Recipe]]
- inspired_by [[Bakery-Style Cookies]]
- pairs_with [[Homemade Ice Cream]]
```
### Creating Effective Relations
When creating relations, you can:
1. Reference existing entities by their exact title
2. Create forward references to entities that don't exist yet
```python
# Example workflow for creating notes with effective relations
async def create_note_with_effective_relations():
# Search for existing entities to reference
search_results = await search("travel")
existing_entities = [result.title for result in search_results.primary_results]
# Check if specific entities exist
packing_tips_exists = "Packing Tips" in existing_entities
japan_travel_exists = "Japan Travel Guide" in existing_entities
# Prepare relations section - include both existing and forward references
relations_section = "## Relations\n"
# Existing reference - exact match to known entity
if packing_tips_exists:
relations_section += "- references [[Packing Tips]]\n"
else:
# Forward reference - will be linked when that entity is created later
relations_section += "- references [[Packing Tips]]\n"
# Another possible reference
if japan_travel_exists:
relations_section += "- part_of [[Japan Travel Guide]]\n"
# You can also check recently modified notes to reference them
recent = await recent_activity(timeframe="1 week")
recent_titles = [item.title for item in recent.primary_results]
if "Transportation Options" in recent_titles:
relations_section += "- relates_to [[Transportation Options]]\n"
# Always include meaningful forward references, even if they don't exist yet
relations_section += "- located_in [[Tokyo]]\n"
relations_section += "- visited_during [[Spring 2023 Trip]]\n"
# Now create the note with both verified and forward relations
content = f"""# Tokyo Neighborhood Guide
## Overview
Details about different Tokyo neighborhoods and their unique characteristics.
## Observations
- [area] Shibuya is a busy shopping district #shopping
- [transportation] Yamanote Line connects major neighborhoods #transit
- [recommendation] Visit Shimokitazawa for vintage shopping #unique
- [tip] Get a Suica card for easy train travel #convenience
{relations_section}
"""
result = await write_note(
title="Tokyo Neighborhood Guide",
content=content,
verbose=True
)
# You can check which relations were resolved and which are forward references
if result and 'relations' in result:
resolved = [r['to_name'] for r in result['relations'] if r.get('target_id')]
forward_refs = [r['to_name'] for r in result['relations'] if not r.get('target_id')]
print(f"Resolved relations: {resolved}")
print(f"Forward references that will be resolved later: {forward_refs}")
```
## Error Handling
Common issues to watch for:
1. **Missing Content**
```python
try:
content = await read_note("Document")
except:
# Try search instead
results = await search("Document")
if results and results.primary_results:
# Found something similar
content = await read_note(results.primary_results[0].permalink)
```
2. **Forward References (Unresolved Relations)**
```python
response = await write_note(..., verbose=True)
# Check for forward references (unresolved relations)
forward_refs = []
for relation in response.get('relations', []):
if not relation.get('target_id'):
forward_refs.append(relation.get('to_name'))
if forward_refs:
# This is a feature, not an error! Inform the user about forward references
print(f"Note created with forward references to: {forward_refs}")
print("These will be automatically linked when those notes are created.")
# Optionally suggest creating those entities now
print("Would you like me to create any of these notes now to complete the connections?")
```
3. **Sync Issues**
```python
# If information seems outdated
activity = await recent_activity(timeframe="1 hour")
if not activity or not activity.primary_results:
print("It seems there haven't been recent updates. You might need to run 'basic-memory sync'.")
```
## Best Practices
1. **Proactively Record Context**
- Offer to capture important discussions
- Record decisions, rationales, and conclusions
- Link to related topics
- Ask for permission first: "Would you like me to save our discussion about [topic]?"
- Confirm when complete: "I've saved our discussion to Basic Memory"
2. **Create a Rich Semantic Graph**
- **Add meaningful observations**: Include at least 3-5 categorized observations in each note
- **Create deliberate relations**: Connect each note to at least 2-3 related entities
- **Use existing entities**: Before creating a new relation, search for existing entities
- **Verify wikilinks**: When referencing `[[Entity]]`, use exact titles of existing notes
- **Check accuracy**: Use `search()` or `recent_activity()` to confirm entity titles
- **Use precise relation types**: Choose specific relation types that convey meaning (e.g., "implements" instead of "relates_to")
- **Consider bidirectional relations**: When appropriate, create inverse relations in both entities
3. **Structure Content Thoughtfully**
- Use clear, descriptive titles
- Organize with logical sections (Context, Decision, Implementation, etc.)
- Include relevant context and background
- Add semantic observations with appropriate categories
- Use a consistent format for similar types of notes
- Balance detail with conciseness
4. **Navigate Knowledge Effectively**
- Start with specific searches
- Follow relation paths
- Combine information from multiple sources
- Verify information is current
- Build a complete picture before responding
5. **Help Users Maintain Their Knowledge**
- Suggest organizing related topics
- Identify potential duplicates
- Recommend adding relations between topics
- Offer to create summaries of scattered information
- Suggest potential missing relations: "I notice this might relate to [topic], would you like me to add that connection?"
Built with ♥️ by Basic Machines
-289
View File
@@ -1,289 +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
### sync
Keeps files and the knowledge graph in sync:
```bash
# Basic sync
basic-memory sync
# Watch for changes
basic-memory sync --watch
# Sync specific folder
basic-memory sync path/to/folder
```
Options:
- `--watch`: Continuously monitor for changes
- `--verbose`: Show detailed output
- `PATH`: Optional path to sync (defaults to ~/basic-memory)
### import
Imports external knowledge sources:
```bash
# Claude conversations
basic-memory import claude conversations
# Claude projects
basic-memory import claude projects
# ChatGPT history
basic-memory import chatgpt
```
Options:
- `--folder PATH`: Target folder for imported content
- `--overwrite`: Replace existing files
- `--skip-existing`: Keep existing files
### status
Shows system status information:
```bash
# Basic status check
basic-memory status
# Detailed status
basic-memory status --verbose
# JSON output
basic-memory status --json
```
### project
Create multiple projects to manage your knowledge.
```bash
# List all configured projects
basic-memory project list
# Add a new project
basic-memory project add work ~/work-basic-memory
# Set the default project
basic-memory project default work
# Remove a project (doesn't delete files)
basic-memory project remove personal
# Show current project
basic-memory project current
```
> Be sure to restart Claude Desktop after changing projects.
#### Using Projects in Commands
All commands support the `--project` flag to specify which project to use:
```bash
# Sync a specific project
basic-memory --project=work sync
# Run MCP server for a specific project
basic-memory --project=personal mcp
```
You can also set the `BASIC_MEMORY_PROJECT` environment variable:
```bash
BASIC_MEMORY_PROJECT=work basic-memory sync
```
### help
The full list of commands and help for each can be viewed with the `--help` argument.
```
✗ basic-memory --help
Usage: basic-memory [OPTIONS] COMMAND [ARGS]...
Basic Memory - Local-first personal knowledge management system.
╭─ Options ─────────────────────────────────────────────────────────────────────────────────╮
│ --project -p TEXT Specify which project to use │
│ [env var: BASIC_MEMORY_PROJECT] │
│ [default: None] │
│ --version -V Show version information and exit. │
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to copy it or │
│ customize the installation. │
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────────────────╮
│ sync Sync knowledge files with the database. │
│ status Show sync status between files and database. │
│ reset Reset database (drop all tables and recreate). │
│ mcp Run the MCP server for Claude Desktop integration. │
│ import Import data from various sources │
│ tool Direct access to MCP tools via CLI │
│ project Manage multiple Basic Memory projects │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
```
## Initial Setup
```bash
# Install Basic Memory
uv install basic-memory
# First sync
basic-memory sync
# Start watching mode
basic-memory sync --watch
```
> **Important**: You need to install Basic Memory via `uv` or `pip` to use the command line tools, see [[Getting Started with Basic Memory#Installation]].
## Regular Usage
```bash
# Check status
basic-memory status
# Import new content
basic-memory import claude conversations
# Sync changes
basic-memory sync
# Sync changes continuously
basic-memory sync --watch
```
## Maintenance Tasks
```bash
# Check system status in detail
basic-memory status --verbose
# Full resync of all files
basic-memory sync
# Import updates to specific folder
basic-memory import claude conversations --folder new
```
## Using stdin with Basic Memory's `write_note` Tool
The `write-note` tool supports reading content from standard input (stdin), allowing for more flexible workflows when creating or updating notes in your Basic Memory knowledge base.
### Use Cases
This feature is particularly useful for:
1. **Piping output from other commands** directly into Basic Memory notes
2. **Creating notes with multi-line content** without having to escape quotes or special characters
3. **Integrating with AI assistants** like Claude Code that can generate content and pipe it to Basic Memory
4. **Processing text data** from files or other sources
### Basic Usage
#### Method 1: Using a Pipe
You can pipe content from another command into `write_note`:
```bash
# Pipe output of a command into a new note
echo "# My Note\n\nThis is a test note" | basic-memory tool write-note --title "Test Note" --folder "notes"
# Pipe output of a file into a new note
cat README.md | basic-memory tool write-note --title "Project README" --folder "documentation"
# Process text through other tools before saving as a note
cat data.txt | grep "important" | basic-memory tool write-note --title "Important Data" --folder "data"
```
#### Method 2: Using Heredoc Syntax
For multi-line content, you can use heredoc syntax:
```bash
# Create a note with heredoc
cat << EOF | basic-memory tool write_note --title "Project Ideas" --folder "projects"
# Project Ideas for Q2
## AI Integration
- Improve recommendation engine
- Add semantic search to product catalog
## Infrastructure
- Migrate to Kubernetes
- Implement CI/CD pipeline
EOF
```
#### Method 3: Input Redirection
You can redirect input from a file:
```bash
# Create a note from file content
basic-memory tool write-note --title "Meeting Notes" --folder "meetings" < meeting_notes.md
```
#### Integration with Claude Code
This feature works well with Claude Code in the terminal:
In a Claude Code session, let Claude know he can use the basic-memory tools, then he can execute them via the cli:
```
⏺ Bash(echo "# Test Note from Claude\n\nThis is a test note created by Claude to test the stdin functionality." | basic-memory tool write-note --title "Claude Test Note" --folder "test" --tags "test" --tags "claude")…
  ⎿  # Created test/Claude Test Note.md (23e00eec)
permalink: test/claude-test-note
## Tags
- test, claude
```
## Troubleshooting Common Issues
### Sync Conflicts
If you encounter a file changed during sync error:
1. Check the file referenced in the error message
2. Resolve any conflicts manually
3. Run sync again
### Import Errors
If import fails:
1. Check that the source file is in the correct format
2. Verify permissions on the target directory
3. Use --verbose flag for detailed error information
### Status Issues
If status shows problems:
1. Note any unresolved relations or warnings
2. Run a full sync to attempt automatic resolution
3. Check file permissions if database access errors occur
## Relations
- used_by [[Getting Started with Basic Memory]] (Installation instructions)
- complements [[User Guide]] (How to use Basic Memory)
- relates_to [[Introduction to Basic Memory]] (System overview)
-107
View File
@@ -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)
+365
View File
@@ -0,0 +1,365 @@
# Docker Setup Guide
Basic Memory can be run in Docker containers to provide a consistent, isolated environment for your knowledge management
system. This is particularly useful for integrating with existing Dockerized MCP servers or for deployment scenarios.
## Quick Start
### Option 1: Using Pre-built Images (Recommended)
Basic Memory provides pre-built Docker images on GitHub Container Registry that are automatically updated with each release.
1. **Use the official image directly:**
```bash
docker run -d \
--name basic-memory-server \
-p 8000:8000 \
-v /path/to/your/obsidian-vault:/app/data:rw \
-v basic-memory-config:/app/.basic-memory:rw \
ghcr.io/basicmachines-co/basic-memory:latest
```
2. **Or use Docker Compose with the pre-built image:**
```yaml
version: '3.8'
services:
basic-memory:
image: ghcr.io/basicmachines-co/basic-memory:latest
container_name: basic-memory-server
ports:
- "8000:8000"
volumes:
- /path/to/your/obsidian-vault:/app/data:rw
- basic-memory-config:/app/.basic-memory:rw
environment:
- BASIC_MEMORY_DEFAULT_PROJECT=main
restart: unless-stopped
```
### Option 2: Using Docker Compose (Building Locally)
1. **Clone the repository:**
```bash
git clone https://github.com/basicmachines-co/basic-memory.git
cd basic-memory
```
2. **Update the docker-compose.yml:**
Edit the volume mount to point to your Obsidian vault:
```yaml
volumes:
# Change './obsidian-vault' to your actual directory path
- /path/to/your/obsidian-vault:/app/data:rw
```
3. **Start the container:**
```bash
docker-compose up -d
```
### Option 3: Using Docker CLI
```bash
# Build the image
docker build -t basic-memory .
# Run with volume mounting
docker run -d \
--name basic-memory-server \
-v /path/to/your/obsidian-vault:/app/data:rw \
-v basic-memory-config:/app/.basic-memory:rw \
-e BASIC_MEMORY_DEFAULT_PROJECT=main \
basic-memory
```
## Configuration
### Volume Mounts
Basic Memory requires several volume mounts for proper operation:
1. **Knowledge Directory** (Required):
```yaml
- /path/to/your/obsidian-vault:/app/data:rw
```
Mount your Obsidian vault or knowledge base directory.
2. **Configuration and Database** (Recommended):
```yaml
- basic-memory-config:/app/.basic-memory:rw
```
Persistent storage for configuration and SQLite database.
You can edit the basic-memory config.json file located in the /app/.basic-memory/config.json after Basic Memory starts.
3. **Multiple Projects** (Optional):
```yaml
- /path/to/project1:/app/data/project1:rw
- /path/to/project2:/app/data/project2:rw
```
You can edit the basic-memory config.json file located in the /app/.basic-memory/config.json
## CLI Commands via Docker
You can run Basic Memory CLI commands inside the container using `docker exec`:
### Basic Commands
```bash
# Check status
docker exec basic-memory-server basic-memory status
# Sync files
docker exec basic-memory-server basic-memory sync
# Show help
docker exec basic-memory-server basic-memory --help
```
### Managing Projects with Volume Mounts
When using Docker volumes, you'll need to configure projects to point to your mounted directories:
1. **Check current configuration:**
```bash
docker exec basic-memory-server cat /app/.basic-memory/config.json
```
2. **Add a project for your mounted volume:**
```bash
# If you mounted /path/to/your/vault to /app/data
docker exec basic-memory-server basic-memory project create my-vault /app/data
# Set it as default
docker exec basic-memory-server basic-memory project set-default my-vault
```
3. **Sync the new project:**
```bash
docker exec basic-memory-server basic-memory sync
```
### Example: Setting up an Obsidian Vault
If you mounted your Obsidian vault like this in docker-compose.yml:
```yaml
volumes:
- /Users/yourname/Documents/ObsidianVault:/app/data:rw
```
Then configure it:
```bash
# Create project pointing to mounted vault
docker exec basic-memory-server basic-memory project create obsidian /app/data
# Set as default
docker exec basic-memory-server basic-memory project set-default obsidian
# Sync to index all files
docker exec basic-memory-server basic-memory sync
```
### Environment Variables
Configure Basic Memory using environment variables:
```yaml
environment:
# Default project
- BASIC_MEMORY_DEFAULT_PROJECT=main
# Enable real-time sync
- BASIC_MEMORY_SYNC_CHANGES=true
# Logging level
- BASIC_MEMORY_LOG_LEVEL=INFO
# Sync delay in milliseconds
- BASIC_MEMORY_SYNC_DELAY=1000
```
## File Permissions
### Linux/macOS
The Docker container now runs as a non-root user to avoid file ownership issues. By default, the container uses UID/GID 1000, but you can customize this to match your user:
```bash
# Build with custom UID/GID to match your user
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t basic-memory .
# Or use docker-compose with build args
```
**Example docker-compose.yml with custom user:**
```yaml
version: '3.8'
services:
basic-memory:
build:
context: .
dockerfile: Dockerfile
args:
UID: 1000 # Replace with your UID
GID: 1000 # Replace with your GID
container_name: basic-memory-server
ports:
- "8000:8000"
volumes:
- /path/to/your/obsidian-vault:/app/data:rw
- basic-memory-config:/app/.basic-memory:rw
environment:
- BASIC_MEMORY_DEFAULT_PROJECT=main
restart: unless-stopped
```
**Using pre-built images:**
If using the pre-built image from GitHub Container Registry, files will be created with UID/GID 1000. You can either:
1. Change your local directory ownership to match:
```bash
sudo chown -R 1000:1000 /path/to/your/obsidian-vault
```
2. Or build your own image with custom UID/GID as shown above.
### Windows
When using Docker Desktop on Windows, ensure the directories are shared:
1. Open Docker Desktop
2. Go to Settings → Resources → File Sharing
3. Add your knowledge directory path
4. Apply & Restart
## Troubleshooting
### Common Issues
1. **File Watching Not Working:**
- Ensure volume mounts are read-write (`:rw`)
- Check directory permissions
- On Linux, may need to increase inotify limits:
```bash
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```
2. **Configuration Not Persisting:**
- Use named volumes for `/app/.basic-memory`
- Check volume mount permissions
3. **Network Connectivity:**
- For HTTP transport, ensure port 8000 is exposed
- Check firewall settings
### Debug Mode
Run with debug logging:
```yaml
environment:
- BASIC_MEMORY_LOG_LEVEL=DEBUG
```
View logs:
```bash
docker-compose logs -f basic-memory
```
## Security Considerations
1. **Docker Security:**
The container runs as a non-root user (UID/GID 1000 by default) for improved security. You can customize the user ID using build arguments to match your local user.
2. **Volume Permissions:**
Ensure mounted directories have appropriate permissions and don't expose sensitive data. With the non-root container, files will be created with the specified user ownership.
3. **Network Security:**
If using HTTP transport, consider using reverse proxy with SSL/TLS and authentication if the endpoint is available on
a network.
4. **IMPORTANT:** The HTTP endpoints have no authorization. They should not be exposed on a public network.
## Integration Examples
### Claude Desktop with Docker
The recommended way to connect Claude Desktop to the containerized Basic Memory is using `mcp-proxy`, which converts the HTTP transport to STDIO that Claude Desktop expects:
1. **Start the Docker container:**
```bash
docker-compose up -d
```
2. **Configure Claude Desktop** to use mcp-proxy:
```json
{
"mcpServers": {
"basic-memory": {
"command": "uvx",
"args": [
"mcp-proxy",
"http://localhost:8000/mcp"
]
}
}
}
```
## Support
For Docker-specific issues:
1. Check the [troubleshooting section](#troubleshooting) above
2. Review container logs: `docker-compose logs basic-memory`
3. Verify volume mounts: `docker inspect basic-memory-server`
4. Test file permissions: `docker exec basic-memory-server ls -la /app`
For general Basic Memory support, see the main [README](../README.md)
and [documentation](https://memory.basicmachines.co/).
## GitHub Container Registry Images
### Available Images
Pre-built Docker images are available on GitHub Container Registry at [`ghcr.io/basicmachines-co/basic-memory`](https://github.com/basicmachines-co/basic-memory/pkgs/container/basic-memory).
**Supported architectures:**
- `linux/amd64` (Intel/AMD x64)
- `linux/arm64` (ARM64, including Apple Silicon)
**Available tags:**
- `latest` - Latest stable release
- `v0.13.8`, `v0.13.7`, etc. - Specific version tags
- `v0.13`, `v0.12`, etc. - Major.minor tags
### Automated Builds
Docker images are automatically built and published when new releases are tagged:
1. **Release Process:** When a git tag matching `v*` (e.g., `v0.13.8`) is pushed, the CI workflow automatically:
- Builds multi-platform Docker images
- Pushes to GitHub Container Registry with appropriate tags
- Uses native GitHub integration for seamless publishing
2. **CI/CD Pipeline:** The Docker workflow includes:
- Multi-platform builds (AMD64 and ARM64)
- Layer caching for faster builds
- Automatic tagging with semantic versioning
- Security scanning and optimization
### Setup Requirements (For Maintainers)
GitHub Container Registry integration is automatic for this repository:
1. **No external setup required** - GHCR is natively integrated with GitHub
2. **Automatic permissions** - Uses `GITHUB_TOKEN` with `packages: write` permission
3. **Public by default** - Images are automatically public for public repositories
The Docker CI workflow (`.github/workflows/docker.yml`) handles everything automatically when version tags are pushed.
-267
View File
@@ -1,267 +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
### 1. Install Basic Memory
```bash
# Install with uv (recommended).
uv tool install basic-memory
# Or with pip
pip install basic-memory
```
> **Important**: You need to install Basic Memory using one of the commands above to use the command line tools.
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
Claude Desktop often has trouble finding executables in your user path. Follow these steps for a reliable setup:
#### Step 1: Find the absolute path to uvx
Open Terminal and run:
```bash
which uvx
```
This will show you the full path (e.g., `/Users/yourusername/.cargo/bin/uvx`).
#### Step 2: Edit Claude Desktop Configuration
Edit the configuration file located at `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"basic-memory": {
"command": "/absolute/path/to/uvx",
"args": [
"basic-memory",
"mcp"
]
}
}
}
```
Replace `/absolute/path/to/uvx` with the actual path you found in Step 1.
> **Note**: Using absolute paths is necessary because Claude Desktop cannot access binaries in your user PATH.
#### Step 3: Restart Claude Desktop
Close and reopen Claude Desktop for the changes to take effect.
### 3. Start the Sync Service
Start the sync service to monitor your files for changes:
```bash
# One-time sync
basic-memory sync
# For continuous monitoring (recommended)
basic-memory sync --watch
```
The `--watch` flag enables automatic detection of file changes, keeping your knowledge base current.
### 4. Staying Updated
To update Basic Memory when new versions are released:
```bash
# Update with uv (recommended)
uv tool upgrade basic-memory
# Or with pip
pip install --upgrade basic-memory
```
> **Note**: After updating, you'll need to restart Claude Desktop and your sync process for changes to take effect.
## Troubleshooting Installation
### Common Issues
#### Claude Says "No Basic Memory Tools Available"
If Claude cannot find Basic Memory tools:
1. **Check absolute paths**: Ensure you're using complete absolute paths to uvx in the Claude Desktop configuration
2. **Verify installation**: Run `basic-memory --version` in Terminal to confirm Basic Memory is installed
3. **Restart applications**: Restart both Terminal and Claude Desktop after making configuration changes
4. **Check sync status**: Ensure `basic-memory sync --watch` is running
#### Permission Issues
If you encounter permission errors:
1. Check that Basic Memory has access to create files in your home directory
2. Ensure Claude Desktop has permission to execute the uvx command
## Creating Your First Knowledge Note
1. **Start the sync process** in a Terminal window:
```bash
basic-memory sync --watch
```
Keep this running in the background.
2. **Open Claude Desktop** and start a new conversation.
3. **Have a natural conversation** about any topic:
```
You: "Let's talk about coffee brewing methods I've been experimenting with."
Claude: "I'd be happy to discuss coffee brewing methods..."
You: "I've found that pour over gives more flavor clarity than French press..."
```
4. **Ask Claude to create a note**:
```
You: "Could you create a note summarizing what we've discussed about coffee brewing?"
```
5. **Confirm note creation**:
Claude will confirm when the note has been created and where it's stored.
6. **View the created file** in your `~/basic-memory` directory using any text editor or Obsidian.
The file structure will look similar to:
```markdown
---
title: Coffee Brewing Methods
permalink: coffee-brewing-methods
---
# Coffee Brewing Methods
## Observations
- [method] Pour over provides more clarity...
- [technique] Water temperature at 205°F...
## Relations
- relates_to [[Other Coffee Topics]]
```
## Using Special Prompts
Basic Memory includes special prompts that help you start conversations with context from your knowledge base:
### Continue Conversation
To resume a previous topic:
```
You: "Let's continue our conversation about coffee brewing."
```
This prompt triggers Claude to:
1. Search your knowledge base for relevant content about coffee brewing
2. Build context from these documents
3. Resume the conversation with full awareness of previous discussions
### Recent Activity
To see what you've been working on:
```
You: "What have we been discussing recently?"
```
This prompt causes Claude to:
1. Retrieve documents modified in the recent past
2. Summarize the topics and main points
3. Offer to continue any of those discussions
### Search
To find specific information:
```
You: "Find information about pour over coffee methods."
```
Claude will:
1. Search your knowledge base for relevant documents
2. Summarize the key findings
3. Offer to explore specific documents in more detail
See [[User Guide#Using Special Prompts]] for further information.
## Using Your Knowledge Base
### Referencing Knowledge
In future conversations, reference your existing knowledge:
```
You: "What water temperature did we decide was optimal for coffee brewing?"
```
Or directly reference notes using memory:// URLs:
```
You: "Take a look at memory://coffee-brewing-methods and let's discuss how to improve my technique."
```
### Building On Previous Knowledge
Basic Memory enables continuous knowledge building:
1. **Reference previous discussions** in new conversations
2. **Add to existing notes** through conversations
3. **Create connections** between related topics
4. **Follow relationships** to build comprehensive context
## Importing Existing Conversations
Import your existing AI conversations:
```bash
# From Claude
basic-memory import claude conversations
# From ChatGPT
basic-memory import chatgpt
```
After importing, run `basic-memory sync` to index everything.
## Quick Tips
- Keep `basic-memory sync --watch` running in a terminal window
- Use special prompts (Continue Conversation, Recent Activity, Search) to start contextual discussions
- Build connections between notes for a richer knowledge graph
- Use direct memory:// URLs when you need precise context
- Use git to version control your knowledge base
- Review and edit AI-generated notes for accuracy
## Next Steps
After getting started, explore these areas:
1. **Read the [[User Guide]]** for comprehensive usage instructions
2. **Understand the [[Knowledge Format]]** to learn how knowledge is structured
3. **Set up [[Obsidian Integration]]** for visual knowledge navigation
4. **Learn about [[Canvas]]** visualizations for mapping concepts
5. **Review the [[CLI Reference]]** for command line tools
-195
View File
@@ -1,195 +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.
### 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)
-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)
-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() build_context()"| KnowledgeIndex
KnowledgeFiles <-.->|Sync Process| KnowledgeIndex
KnowledgeFiles <-->|Direct Editing| Editors((Text Editors & Git))
User -.->|"Complete control, Privacy preserved"| KnowledgeFiles
class Claude primary
class BMCP secondary
class KnowledgeFiles tertiary
class KnowledgeIndex quaternary
class User,Editors user`;
```
### Sqlite Database
A local SQLite database maintains the knowledge graph topology for fast queries and semantic traversal without cloud dependencies. It contains:
- db tables for the knowledge graph schema
- a search index table enabling full text search across the knowledge base
### Sync Process
The sync process:
1. Detects changes to files in the knowledge directory
2. Parses modified files to extract structured data
3. Updates the SQLite database with changes
4. Resolves forward references when new entities are created
5. Updates the search index for fast querying
### Search Engine
The search functionality:
1. Uses a combination of full-text search and semantic matching
2. Indexes observations, relations, and content
3. Supports wildcards and pattern matching in memory:// URLs
4. Traverses the knowledge graph to follow relationships
5. Ranks results by relevance to the query
## Relations
- relates_to [[Welcome to Basic memory]] (Overview)
- relates_to [[CLI Reference]] (Command line tools)
- implements [[Knowledge Format]] (File structure and format)
-556
View File
@@ -1,556 +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
### Importing External Knowledge
Import existing conversations:
```bash
# From Claude
basic-memory import claude conversations
# From ChatGPT
basic-memory import chatgpt
```
After importing, run `basic-memory sync` to index everything.
### Obsidian Integration
Use with [Obsidian](https://obsidian.md):
1. Point Obsidian to your `~/basic-memory` directory
2. Use Obsidian's graph view to visualize your knowledge network
3. All changes sync back to Basic Memory
### Canvas Visualizations
Create visual knowledge maps:
```
You: Could you create a canvas visualization of our project components?
```
This generates an Obsidian canvas file showing the relationships between concepts.
### Advanced Memory URI Patterns
Use wildcards and patterns:
```
You: Review memory://project/*/requirements to summarize all project requirements.
```
## Command Line Interface
### Sync Commands
```bash
# One-time sync
basic-memory sync
# Watch for changes
basic-memory sync --watch
```
### Status and Information
```bash
# Check system status
basic-memory status
# View CLI help
basic-memory --help
```
### Import Commands
```bash
# Import from Claude
basic-memory import claude conversations
# Import from ChatGPT
basic-memory import chatgpt
```
## Multiple Projects
Basic Memory supports managing multiple separate knowledge bases through projects. This feature allows you to maintain
separate knowledge graphs for different purposes (e.g., personal notes, work projects, research topics).
Basic Memory keeps a list of projects in a config file: ` ~/.basic-memory/config.json`
### Managing Projects
```bash
# List all configured projects
basic-memory project list
# Add a new project
basic-memory project add work ~/work-basic-memory
# Set the default project
basic-memory project default work
# Remove a project (doesn't delete files)
basic-memory project remove personal
# Show current project
basic-memory project current
```
### Using Projects in Commands
All commands support the `--project` flag to specify which project to use:
```bash
# Sync a specific project
basic-memory --project=work sync
# Run MCP server for a specific project
basic-memory --project=personal mcp
```
You can also set the `BASIC_MEMORY_PROJECT` environment variable:
```bash
BASIC_MEMORY_PROJECT=work basic-memory sync
```
### Project Isolation
Each project maintains:
- Its own collection of markdown files in the specified directory
- A separate SQLite database for that project
- Complete knowledge graph isolation from other projects
## Workflow Tips
1. Run sync in watch mode for automatic updates
2. Use git for version control of your knowledge base
3. Review and edit AI-created content for accuracy
4. Periodically organize and refine your knowledge structure
5. Build rich connections between related ideas
6. Use forward references to plan future documentation
7. Start conversations with special prompts to leverage existing knowledge
## Troubleshooting
### Sync Issues
If changes aren't showing up:
1. Verify `basic-memory sync --watch` is running
2. Run `basic-memory status` to check system state
3. Try a manual sync with `basic-memory sync`
### Missing Content
If content isn't found:
1. Check the exact path and permalink
2. Try searching with more general terms
3. Verify the file exists in your knowledge base
### Relation Problems
If relations aren't working:
1. Ensure exact title matching in [[WikiLinks]]
2. Check for typos in relation types
3. Verify both documents exist
## Relations
- implements [[Knowledge Format]] (How knowledge is structured)
- relates_to [[Getting Started with Basic Memory]] (Setup and first steps)
- relates_to [[Canvas]] (Creating visual knowledge maps)
- relates_to [[CLI Reference]] (Command line tools)
-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.

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: 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

+241
View File
@@ -0,0 +1,241 @@
# Character Handling and Conflict Resolution
Basic Memory handles various character encoding scenarios and file naming conventions to provide consistent permalink generation and conflict resolution. This document explains how the system works and how to resolve common character-related issues.
## Overview
Basic Memory uses a sophisticated system to generate permalinks from file paths while maintaining consistency across different operating systems and character encodings. The system normalizes file paths and generates unique permalinks to prevent conflicts.
## Character Normalization Rules
### 1. Permalink Generation
When Basic Memory processes a file path, it applies these normalization rules:
```
Original: "Finance/My Investment Strategy.md"
Permalink: "finance/my-investment-strategy"
```
**Transformation process:**
1. Remove file extension (`.md`)
2. Convert to lowercase (case-insensitive)
3. Replace spaces with hyphens
4. Replace underscores with hyphens
5. Handle international characters (transliteration for Latin, preservation for non-Latin)
6. Convert camelCase to kebab-case
### 2. International Character Support
**Latin characters with diacritics** are transliterated:
- `ø``o` (Søren → soren)
- `ü``u` (Müller → muller)
- `é``e` (Café → cafe)
- `ñ``n` (Niño → nino)
**Non-Latin characters** are preserved:
- Chinese: `中文/测试文档.md``中文/测试文档`
- Japanese: `日本語/文書.md``日本語/文書`
## Common Conflict Scenarios
### 1. Hyphen vs Space Conflicts
**Problem:** Files with existing hyphens conflict with generated permalinks from spaces.
**Example:**
```
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug" (CONFLICT!)
```
**Resolution:** The system automatically resolves this by adding suffixes:
```
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug-1"
```
**Best Practice:** Choose consistent naming conventions within your project.
### 2. Case Sensitivity Conflicts
**Problem:** Different case variations that normalize to the same permalink.
**Example on macOS:**
```
Directory: Finance/investment.md
Directory: finance/investment.md (different on filesystem, same permalink)
```
**Resolution:** Basic Memory detects case conflicts and prevents them during sync operations with helpful error messages.
**Best Practice:** Use consistent casing for directory and file names.
### 3. Character Encoding Conflicts
**Problem:** Different Unicode normalizations of the same logical character.
**Example:**
```
File 1: "café.md" (é as single character)
File 2: "café.md" (e + combining accent)
```
**Resolution:** Basic Memory normalizes Unicode characters using NFD normalization to detect these conflicts.
### 4. Forward Slash Conflicts
**Problem:** Forward slashes in frontmatter or file names interpreted as path separators.
**Example:**
```yaml
---
permalink: finance/investment/strategy
---
```
**Resolution:** Basic Memory validates frontmatter permalinks and warns about path separator conflicts.
## Error Messages and Troubleshooting
### "UNIQUE constraint failed: entity.file_path, entity.project_id"
**Cause:** Two entities trying to use the same file path within a project.
**Common scenarios:**
1. File move operation where destination is already occupied
2. Case sensitivity differences on macOS
3. Character encoding conflicts
4. Concurrent file operations
**Resolution steps:**
1. Check for duplicate file names with different cases
2. Look for files with similar names but different character encodings
3. Rename conflicting files to have unique names
4. Run sync again after resolving conflicts
### "File path conflict detected during move"
**Cause:** Enhanced conflict detection preventing potential database integrity violations.
**What this means:** The system detected that moving a file would create a conflict before attempting the database operation.
**Resolution:** Follow the specific guidance in the error message, which will indicate the type of conflict detected.
## Best Practices
### 1. File Naming Conventions
**Recommended patterns:**
- Use consistent casing (prefer lowercase)
- Use hyphens instead of spaces for multi-word files
- Avoid special characters that could conflict with path separators
- Be consistent with directory structure casing
**Examples:**
```
✅ Good:
- finance/investment-strategy.md
- projects/basic-memory-features.md
- docs/api-reference.md
❌ Problematic:
- Finance/Investment Strategy.md (mixed case, spaces)
- finance/Investment Strategy.md (inconsistent case)
- docs/API/Reference.md (mixed case directories)
```
### 2. Permalink Management
**Custom permalinks in frontmatter:**
```yaml
---
type: knowledge
permalink: custom-permalink-name
---
```
**Guidelines:**
- Use lowercase permalinks
- Use hyphens for word separation
- Avoid path separators unless creating sub-paths
- Ensure uniqueness within your project
### 3. Directory Structure
**Consistent casing:**
```
✅ Good:
finance/
investment-strategies.md
portfolio-management.md
❌ Problematic:
Finance/ (capital F)
investment-strategies.md
finance/ (lowercase f)
portfolio-management.md
```
## Migration and Cleanup
### Identifying Conflicts
Use Basic Memory's built-in conflict detection:
```bash
# Sync will report conflicts
basic-memory sync
# Check sync status for warnings
basic-memory status
```
### Resolving Existing Conflicts
1. **Identify conflicting files** from sync error messages
2. **Choose consistent naming convention** for your project
3. **Rename files** to follow the convention
4. **Re-run sync** to verify resolution
### Bulk Renaming Strategy
For projects with many conflicts:
1. **Backup your project** before making changes
2. **Standardize on lowercase** file and directory names
3. **Replace spaces with hyphens** in file names
4. **Use consistent character encoding** (UTF-8)
5. **Test sync after each batch** of changes
## System Enhancements
### Recent Improvements (v0.13+)
1. **Enhanced conflict detection** before database operations
2. **Improved error messages** with specific resolution guidance
3. **Character normalization utilities** for consistent handling
4. **File swap detection** for complex move scenarios
5. **Proactive conflict warnings** during permalink resolution
### Monitoring and Logging
The system now provides detailed logging for conflict resolution:
```
DEBUG: Detected potential file path conflicts for 'Finance/Investment.md': ['finance/investment.md']
WARNING: File path conflict detected during move: entity_id=123 trying to move from 'old.md' to 'new.md'
```
These logs help identify and resolve conflicts before they cause sync failures.
## Support and Resources
If you encounter character-related conflicts not covered in this guide:
1. **Check the logs** for specific conflict details
2. **Review error messages** for resolution guidance
3. **Report issues** with examples of the conflicting files
4. **Consider the file naming best practices** outlined above
The Basic Memory system is designed to handle most character conflicts automatically while providing clear guidance for manual resolution when needed.
+657
View File
@@ -0,0 +1,657 @@
# Basic Memory Cloud CLI Guide
The Basic Memory Cloud CLI provides seamless integration between local and cloud knowledge bases using a **cloud mode toggle**. When cloud mode is enabled, all your regular `bm` commands work transparently with the cloud instead of locally.
## Overview
The cloud CLI enables you to:
- **Toggle cloud mode** with `bm cloud login` / `bm cloud logout`
- **Use regular commands in cloud mode**: `bm project`, `bm sync`, `bm tool` all work with cloud
- **Bidirectional sync** with rclone bisync (recommended for most users)
- **Direct file access** via rclone mount (alternative workflow)
- **Integrity verification** with `bm cloud check`
- **Automatic project creation** from local directories
## Prerequisites
Before using Basic Memory Cloud, you need:
- **Active Subscription**: An active Basic Memory Cloud subscription is required to access cloud features
- **Subscribe**: Visit [https://basicmemory.com/subscribe](https://basicmemory.com/subscribe) to sign up
If you attempt to log in without an active subscription, you'll receive a "Subscription Required" error with a link to subscribe.
## The Cloud Mode Paradigm
Basic Memory Cloud follows the **Dropbox/iCloud model** - a single cloud space containing all your projects, not per-project connections.
**How it works:**
- One login per machine: `bm cloud login`
- One sync directory: `~/basic-memory-cloud-sync/` (all projects)
- Projects are folders within your cloud space
- All regular commands work in cloud mode
**Why this model:**
- ✅ Single set of credentials (not N per project)
- ✅ One rclone process (not N processes)
- ✅ Familiar pattern (like Dropbox)
- ✅ Simple operations (setup once, sync anytime)
- ✅ Natural scaling (add projects = add folders)
## Quick Start
### 1. Enable Cloud Mode
Authenticate and enable cloud mode for all commands:
```bash
bm cloud login
```
This command will:
1. Open your browser to the Basic Memory Cloud authentication page
2. Prompt you to authorize the CLI application
3. Store your authentication token locally
4. **Enable cloud mode** - all CLI commands now work against cloud
### 2. Set Up Sync
Set up bidirectional file synchronization:
```bash
bm cloud setup
```
This will:
1. Install rclone automatically (if needed)
2. Configure sync credentials
3. Create `~/basic-memory-cloud-sync/` directory
4. Establish initial sync baseline
**Alternative:** Use `bm cloud setup --mount` to set up mount instead of sync.
### 3. Verify Setup
Check that everything is working:
```bash
bm cloud status
```
You should see:
- `Mode: Cloud (enabled)`
- `Cloud instance is healthy`
- Bisync status showing `✓ Initialized`
### 4. Start Using Cloud
Now all your regular commands work with the cloud:
```bash
# List cloud projects
bm project list
# Create cloud project
bm project add "my-research"
# Use MCP tools on cloud
bm tool write-note --title "Hello" --folder "my-research" --content "Test"
# Sync with cloud
bm sync
# Watch mode for continuous sync
bm sync --watch
```
### 5. Disable Cloud Mode
Return to local mode:
```bash
bm cloud logout
```
All commands now work locally again.
## Working with Cloud Projects
**Important:** When cloud mode is enabled, use regular `bm project` commands (not `bm cloud project`).
### Listing Projects
View all projects (cloud projects when cloud mode is enabled):
```bash
# In cloud mode - lists cloud projects
bm project list
# In local mode - lists local projects
bm project list
```
### Creating Projects
Create a new project (creates on cloud when cloud mode is enabled):
```bash
# In cloud mode - creates cloud project
bm project add my-new-project
# Create and set as default
bm project add my-new-project --default
```
### Automatic Project Creation
**New in SPEC-9:** Projects are automatically created when you create local directories!
```bash
# Create a local directory in your sync folder
mkdir ~/basic-memory-cloud-sync/new-project
echo "# Notes" > ~/basic-memory-cloud-sync/new-project/readme.md
# Sync - automatically creates cloud project
bm sync
# Verify - project now exists on cloud
bm project list
```
This Dropbox-like workflow means you don't need to manually coordinate projects between local and cloud.
## File Synchronization
### The `bm sync` Command (Cloud Mode Aware)
The `bm sync` command automatically adapts based on cloud mode:
**In local mode:**
```bash
bm sync # Indexes local files into database
```
**In cloud mode:**
```bash
bm sync # Runs bisync + indexes files
bm sync --watch # Continuous sync every 60 seconds
bm sync --interval 30 # Custom interval
```
The same command works everywhere - no need to remember different commands for local vs cloud!
## Bidirectional Sync (bisync) - Recommended
Bidirectional sync is the **recommended approach** for most users. It provides:
- ✅ Offline access to all files
- ✅ Automatic bidirectional synchronization
- ✅ Conflict detection and resolution
- ✅ Works with any editor or tool
- ✅ Background watch mode
### Setup
Set up bisync (runs automatically if you used `bm cloud setup`):
```bash
bm cloud setup
```
Or set up with custom directory:
```bash
bm cloud setup --dir ~/my-sync-folder
```
### Running Sync
Use the cloud-aware `bm sync` command:
```bash
# Manual sync
bm sync
# Watch mode (continuous sync)
bm sync --watch
# Custom interval (30 seconds)
bm sync --watch --interval 30
```
### Bisync Profiles
Bisync supports three conflict resolution strategies with different safety levels:
| Profile | Conflict Resolution | Max Deletes | Use Case |
|---------|-------------------|-------------|----------|
| **balanced** | newer | 25 | Default, recommended for most users |
| **safe** | none | 10 | Keep both versions on conflict |
| **fast** | newer | 50 | Rapid iteration, higher delete tolerance |
**Profile Details:**
- **safe**:
- Conflict resolution: `none` (creates `.conflict` files for both versions)
- Max delete: 10 files per sync
- Best for: Critical data where you want manual conflict resolution
- **balanced** (default):
- Conflict resolution: `newer` (auto-resolve to most recent file)
- Max delete: 25 files per sync
- Best for: General use with automatic conflict handling
- **fast**:
- Conflict resolution: `newer` (auto-resolve to most recent file)
- Max delete: 50 files per sync
- Best for: Rapid development iteration with less restrictive safety checks
**How to Select a Profile:**
The default profile (`balanced`) is used automatically with `bm sync`:
```bash
# Uses balanced profile (default)
bm sync
```
For advanced control, use `bm cloud bisync` with the `--profile` flag:
```bash
# Use safe mode
bm cloud bisync --profile safe
# Use fast mode
bm cloud bisync --profile fast
# Preview changes with specific profile
bm cloud bisync --profile safe --dry-run
```
**Check Available Profiles:**
```bash
bm cloud status
```
This shows all available profiles with their settings.
**Current Limitations:**
- Profiles are hardcoded and cannot be customized
- No config file option to change default profile
- Profile settings (max_delete, conflict_resolve) cannot be modified without code changes
- Profile selection only available via `bm cloud bisync --profile` (advanced command)
### Establishing New Baseline
If you need to force a complete resync:
```bash
bm cloud bisync --resync
```
**Warning:** This overwrites the sync state. Use only when recovering from errors.
### Checking Sync Status
View current sync status:
```bash
bm cloud status
```
This shows:
- Cloud mode status
- Instance health
- Sync directory location
- Last sync time
- Available bisync profiles
### Verifying Sync Integrity
Check that local and cloud files match:
```bash
# Full integrity check
bm cloud check
# Faster one-way check
bm cloud check --one-way
```
This uses `rclone check` to verify files match without transferring data.
### Working with Bisync
Create and edit files in `~/basic-memory-cloud-sync/`:
```bash
# Create a new note
echo "# My Research" > ~/basic-memory-cloud-sync/my-project/notes.md
# Edit with your favorite editor
code ~/basic-memory-cloud-sync/my-project/
# Sync changes to cloud
bm sync
```
In watch mode, changes sync automatically:
```bash
# Start watch mode
bm sync --watch
# Edit files - they sync automatically every 60 seconds
code ~/basic-memory-cloud-sync/my-project/
```
### Filter Configuration
Bisync uses `.bmignore` patterns from `~/.basic-memory/.bmignore`:
```bash
# View current ignore patterns
cat ~/.basic-memory/.bmignore
# Edit ignore patterns
code ~/.basic-memory/.bmignore
```
Example `.bmignore`:
```gitignore
# This file is used by 'bm cloud bisync' and file sync
# Patterns use standard gitignore-style syntax
# Hidden files (files starting with dot)
- .*
# Basic Memory internal files
- memory.db/**
- memory.db-shm/**
- memory.db-wal/**
- config.json/**
# Version control
- .git/**
# Python
- __pycache__/**
- *.pyc
- .venv/**
# Node.js
- node_modules/**
```
**Key points:**
-**Global configuration** - One ignore file for all projects
-**rclone filter syntax** - Patterns with `- ` prefix
-**Automatic creation** - Created with defaults on first use
-**Shared patterns** - Same patterns used by sync service
## NFS Mount (Direct Access) - Alternative
NFS mount provides direct file system access as an alternative to bisync. Use this if you prefer mounting files like a network drive.
### Setup
Set up mount instead of bisync:
```bash
bm cloud setup --mount
```
### Mounting Files
Mount your cloud files:
```bash
# Mount with default settings
bm cloud mount
# Mount with specific profile
bm cloud mount --profile fast
```
#### Mount Profiles
- **balanced** (default): Balanced caching for general use
- **streaming**: Optimized for large files
- **fast**: Minimal verification for rapid access
### Checking Mount Status
View current mount status:
```bash
bm cloud status --mount
```
### Unmounting Files
Unmount when done:
```bash
bm cloud unmount
```
### Working with Mounted Files
Once mounted, files appear at `~/basic-memory-cloud/`:
```bash
# List cloud files
ls ~/basic-memory-cloud/
# Edit with your favorite editor
code ~/basic-memory-cloud/my-project/
# Changes are immediately synced to cloud
echo "# Notes" > ~/basic-memory-cloud/my-project/readme.md
```
**Note:** Changes are written through to cloud immediately. There's no "sync" step needed.
## Instance Management
### Health Check
Check if your cloud instance is healthy:
```bash
bm cloud status
```
This shows:
- Cloud mode enabled/disabled
- Instance health status
- Instance version
- Sync or mount status
## Troubleshooting
### Authentication Issues
**Problem**: "Authentication failed" or "Invalid token"
**Solution**: Re-authenticate:
```bash
bm cloud logout
bm cloud login
```
### Subscription Issues
**Problem**: "Subscription Required" error when logging in
**Solution**: You need an active Basic Memory Cloud subscription to use cloud features.
1. Visit the subscribe URL shown in the error message
2. Sign up for a subscription
3. Once your subscription is active, run `bm cloud login` again
**Problem**: "Subscription Required" error for existing user
**Solution**: Your subscription may have expired or been cancelled.
1. Check your subscription status at [https://basicmemory.com/account](https://basicmemory.com/account)
2. Renew your subscription if needed
3. Run `bm cloud login` again
Note: Access is immediately restored when your subscription becomes active.
### Sync Issues
**Problem**: "Bisync not initialized"
**Solution**: Run setup or initialize with resync:
```bash
bm cloud setup
# or
bm cloud bisync --resync
```
**Problem**: "Too many deletes" error
**Solution**: Bisync detected many deletions (safety check). Review changes and use a higher delete limit profile or force resync:
```bash
bm cloud bisync --profile fast # Higher delete limit
# or
bm cloud bisync --resync # Force baseline
```
**Problem**: Conflicts detected
**Solution**: Bisync found files changed in both locations. Check sync directory for `.conflict` files:
```bash
ls ~/basic-memory-cloud-sync/**/*.conflict
```
Resolve conflicts manually, then sync again.
### Connection Issues
**Problem**: "Cannot connect to cloud instance"
**Solution**: Check cloud status:
```bash
bm cloud status
```
If instance is down, wait a few minutes and retry. If problem persists, contact support.
### Mount Issues
**Problem**: "Mount point is busy"
**Solution**: Unmount and remount:
```bash
bm cloud unmount
bm cloud mount
```
**Problem**: "Permission denied" when accessing mounted files
**Solution**: Check mount status and remount:
```bash
bm cloud status --mount
bm cloud unmount
bm cloud mount
```
## Security
- **Authentication**: OAuth 2.1 with PKCE flow
- **Tokens**: Stored securely in `~/.basic-memory/auth/token`
- **Transport**: All data encrypted in transit (HTTPS)
- **Credentials**: Scoped S3 credentials for sync/mount (read-write access to your tenant only)
- **Isolation**: Your data is isolated from other tenants
- **Ignore patterns**: Sensitive files (`.env`, credentials) automatically excluded
## Command Reference
### Cloud Mode Management
```bash
bm cloud login # Authenticate and enable cloud mode
bm cloud logout # Disable cloud mode
bm cloud status # Check cloud mode and sync status
bm cloud status --mount # Check cloud mode and mount status
```
### Setup
```bash
bm cloud setup # Setup bisync (default, recommended)
bm cloud setup --mount # Setup mount (alternative)
bm cloud setup --dir ~/sync # Custom sync directory
```
### Project Management (Cloud Mode Aware)
When cloud mode is enabled, these commands work with cloud:
```bash
bm project list # List projects
bm project add <name> # Create project
bm project add <name> --default # Create and set as default
bm project rm <name> # Delete project
bm project set-default <name> # Set default project
```
### File Synchronization
```bash
bm sync # Sync files (local or cloud depending on mode)
bm sync --watch # Continuous sync (cloud mode only)
bm sync --interval 30 # Custom interval for watch mode
# Advanced bisync commands
bm cloud bisync # Run bisync manually
bm cloud bisync --profile safe # Use specific profile
bm cloud bisync --dry-run # Preview changes
bm cloud bisync --resync # Force new baseline
bm cloud bisync --watch # Continuous sync
bm cloud bisync --verbose # Show detailed output
# Integrity verification
bm cloud check # Full integrity check
bm cloud check --one-way # Faster one-way check
```
### Direct File Access (Mount)
```bash
bm cloud mount # Mount cloud files
bm cloud mount --profile fast # Use specific profile
bm cloud unmount # Unmount files
```
## Summary
Basic Memory Cloud provides two workflows:
### Recommended: Bidirectional Sync (bisync)
1. `bm cloud login` - Authenticate once
2. `bm cloud setup` - Configure sync once
3. `bm sync` - Sync anytime (or use `--watch`)
4. Work in `~/basic-memory-cloud-sync/`
5. Changes sync bidirectionally
### Alternative: Direct Mount
1. `bm cloud login` - Authenticate once
2. `bm cloud setup --mount` - Configure mount once
3. `bm cloud mount` - Mount when needed
4. Work in `~/basic-memory-cloud/`
5. Changes write through immediately
Both approaches work seamlessly with cloud mode - all your regular `bm` commands work with either workflow!
-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]]
Binary file not shown.
-26
View File
@@ -1,26 +0,0 @@
# Basic Memory Installer
This installer configures Basic Memory to work with Claude Desktop.
## Installation
1. Download the latest installer from the [releases page](https://github.com/basicmachines-co/basic-memory/releases)
2. Unzip the downloaded file
3. Since the app is currently unsigned, you'll need to:
On your Mac, choose Apple menu > System Settings, then click Privacy & Security in the sidebar. (You may need to
scroll down.)
Go to Security, then click Open.
Click Open Anyway.
This button is available for about an hour after you try to open the app.
Enter your login password, then click OK.
https://support.apple.com/guide/mac-help/apple-cant-check-app-for-malicious-software-mchleab3a043/mac
5. Restart Claude Desktop
The warning only appears the first time you open the app. Future updates will include proper code signing.
-64
View File
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<!-- Background -->
<rect x="0" y="0" width="512" height="512" rx="64" fill="#111111"/>
<!-- Define arrowhead marker -->
<defs>
<marker id="arrowhead"
markerWidth="10"
markerHeight="10"
refX="8"
refY="5"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 Z"
fill="#00cc00"/>
</marker>
</defs>
<!-- State 1 (initial) -->
<circle cx="156" cy="256" r="30" fill="none" stroke="#00cc00" stroke-width="3"/>
<!-- State 2 (accept) -->
<circle cx="356" cy="176" r="34" fill="none" stroke="#00cc00" stroke-width="3"/>
<circle cx="356" cy="176" r="28" fill="none" stroke="#00cc00" stroke-width="3"/>
<!-- State 3 (accept) -->
<circle cx="356" cy="336" r="34" fill="none" stroke="#00cc00" stroke-width="3"/>
<circle cx="356" cy="336" r="28" fill="none" stroke="#00cc00" stroke-width="3"/>
<!-- Initial arrow -->
<path d="M 96 256 L 126 256"
stroke="#00cc00" stroke-width="3" fill="none"
marker-end="url(#arrowhead)"/>
<!-- State transitions -->
<!-- 1 -> 2 -->
<path d="M 180 240
Q 260 200, 320 176"
stroke="#00cc00" stroke-width="3" fill="none"
marker-end="url(#arrowhead)"/>
<!-- 1 -> 3 -->
<path d="M 180 272
Q 260 312, 320 336"
stroke="#00cc00" stroke-width="3" fill="none"
marker-end="url(#arrowhead)"/>
<!-- Self loops -->
<path d="M 356 142
Q 396 142, 396 176
Q 396 210, 356 210
Q 316 210, 316 176
Q 316 142, 356 142"
stroke="#00cc00" stroke-width="2" fill="none"
marker-end="url(#arrowhead)"/>
<path d="M 356 302
Q 396 302, 396 336
Q 396 370, 356 370
Q 316 370, 316 336
Q 316 302, 356 302"
stroke="#00cc00" stroke-width="2" fill="none"
marker-end="url(#arrowhead)"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

-93
View File
@@ -1,93 +0,0 @@
import json
import subprocess
import sys
from pathlib import Path
# Use tkinter for GUI alerts on macOS
if sys.platform == "darwin":
import tkinter as tk
from tkinter import messagebox
def ensure_uv_installed():
"""Check if uv is installed, install if not."""
try:
subprocess.run(["uv", "--version"], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print("Installing uv package manager...")
subprocess.run(
[
"curl",
"-LsSf",
"https://astral.sh/uv/install.sh",
"|",
"sh",
],
shell=True,
)
def get_config_path():
"""Get Claude Desktop config path for current platform."""
if sys.platform == "darwin":
return Path.home() / "Library/Application Support/Claude/claude_desktop_config.json"
elif sys.platform == "win32":
return Path.home() / "AppData/Roaming/Claude/claude_desktop_config.json"
else:
raise RuntimeError(f"Unsupported platform: {sys.platform}")
def update_claude_config():
"""Update Claude Desktop config to include basic-memory."""
config_path = get_config_path()
config_path.parent.mkdir(parents=True, exist_ok=True)
# Load existing config or create new
if config_path.exists():
config = json.loads(config_path.read_text())
else:
config = {"mcpServers": {}}
# Add/update basic-memory config
config["mcpServers"]["basic-memory"] = {
"command": "uvx",
"args": ["basic-memory@latest", "mcp"],
}
# Write back config
config_path.write_text(json.dumps(config, indent=2))
def print_completion_message():
"""Show completion message with helpful tips."""
message = """Installation complete! Basic Memory is now available in Claude Desktop.
Please restart Claude Desktop for changes to take effect.
Quick Start:
1. You can run sync directly using: uvx basic-memory sync
2. Optionally, install globally with: uv pip install basic-memory
Built with ♥️ by Basic Machines."""
if sys.platform == "darwin":
# Show GUI message on macOS
root = tk.Tk()
root.withdraw() # Hide the main window
messagebox.showinfo("Basic Memory", message)
root.destroy()
else:
# Fallback to console output
print(message)
def main():
print("Welcome to Basic Memory installer")
ensure_uv_installed()
print("Configuring Claude Desktop...")
update_claude_config()
print_completion_message()
if __name__ == "__main__":
main()
-27
View File
@@ -1,27 +0,0 @@
#!/bin/bash
# Convert SVG to PNG at various required sizes
rsvg-convert -h 16 -w 16 icon.svg > icon_16x16.png
rsvg-convert -h 32 -w 32 icon.svg > icon_32x32.png
rsvg-convert -h 128 -w 128 icon.svg > icon_128x128.png
rsvg-convert -h 256 -w 256 icon.svg > icon_256x256.png
rsvg-convert -h 512 -w 512 icon.svg > icon_512x512.png
# Create iconset directory
mkdir -p Basic.iconset
# Move files into iconset with Mac-specific names
cp icon_16x16.png Basic.iconset/icon_16x16.png
cp icon_32x32.png Basic.iconset/icon_16x16@2x.png
cp icon_32x32.png Basic.iconset/icon_32x32.png
cp icon_128x128.png Basic.iconset/icon_32x32@2x.png
cp icon_256x256.png Basic.iconset/icon_128x128.png
cp icon_512x512.png Basic.iconset/icon_256x256.png
cp icon_512x512.png Basic.iconset/icon_512x512.png
# Convert iconset to icns
iconutil -c icns Basic.iconset
# Clean up
rm -rf Basic.iconset
rm icon_*.png
-40
View File
@@ -1,40 +0,0 @@
from cx_Freeze import setup, Executable
import sys
# Build options for all platforms
build_exe_options = {
"packages": ["json", "pathlib"],
"excludes": ["unittest", "pydoc", "test"],
}
# Platform-specific options
if sys.platform == "win32":
base = "Win32GUI" # Use GUI base for Windows
build_exe_options.update(
{
"include_msvcr": True,
}
)
target_name = "Basic Memory Installer.exe"
else: # darwin
base = None # Don't use GUI base for macOS
target_name = "Basic Memory Installer"
executables = [
Executable(script="installer.py", target_name=target_name, base=base, icon="Basic.icns")
]
setup(
name="basic-memory",
version=open("../pyproject.toml").read().split('version = "', 1)[1].split('"', 1)[0],
description="Basic Memory - Local-first knowledge management",
options={
"build_exe": build_exe_options,
"bdist_mac": {
"bundle_name": "Basic Memory Installer",
"iconfile": "Basic.icns",
"codesign_identity": "-", # Force ad-hoc signing
},
},
executables=executables,
)
+188
View File
@@ -0,0 +1,188 @@
# Basic Memory - Modern Command Runner
# Install dependencies
install:
pip install -e ".[dev]"
uv sync
@echo ""
@echo "💡 Remember to activate the virtual environment by running: source .venv/bin/activate"
# Run unit tests in parallel
test-unit:
uv run pytest -p pytest_mock -v -n auto
# Run integration tests in parallel
test-int:
uv run pytest -p pytest_mock -v --no-cov -n auto test-int
# Run all tests
test: test-unit test-int
# Lint and fix code (calls fix)
lint: fix
# Lint and fix code
fix:
uv run ruff check --fix --unsafe-fixes src tests
# Type check code
typecheck:
uv run pyright
# Clean build artifacts and cache files
clean:
find . -type f -name '*.pyc' -delete
find . -type d -name '__pycache__' -exec rm -r {} +
rm -rf installer/build/ installer/dist/ dist/
rm -f rw.*.dmg .coverage.*
# Format code with ruff
format:
uv run ruff format .
# Run MCP inspector tool
run-inspector:
npx @modelcontextprotocol/inspector
# Build macOS installer
installer-mac:
cd installer && chmod +x make_icons.sh && ./make_icons.sh
cd installer && uv run python setup.py bdist_mac
# Build Windows installer
installer-win:
cd installer && uv run python setup.py bdist_win32
# Update all dependencies to latest versions
update-deps:
uv sync --upgrade
# Run all code quality checks and tests
check: lint format typecheck test
# Generate Alembic migration with descriptive message
migration message:
cd src/basic_memory/alembic && alembic revision --autogenerate -m "{{message}}"
# Create a stable release (e.g., just release v0.13.2)
release version:
#!/usr/bin/env bash
set -euo pipefail
# Validate version format
if [[ ! "{{version}}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ Invalid version format. Use: v0.13.2"
exit 1
fi
# Extract version number without 'v' prefix
VERSION_NUM=$(echo "{{version}}" | sed 's/^v//')
echo "🚀 Creating stable release {{version}}"
# Pre-flight checks
echo "📋 Running pre-flight checks..."
if [[ -n $(git status --porcelain) ]]; then
echo "❌ Uncommitted changes found. Please commit or stash them first."
exit 1
fi
if [[ $(git branch --show-current) != "main" ]]; then
echo "❌ Not on main branch. Switch to main first."
exit 1
fi
# Check if tag already exists
if git tag -l "{{version}}" | grep -q "{{version}}"; then
echo "❌ Tag {{version}} already exists"
exit 1
fi
# Run quality checks
echo "🔍 Running quality checks..."
just check
# Update version in __init__.py
echo "📝 Updating version in __init__.py..."
sed -i.bak "s/__version__ = \".*\"/__version__ = \"$VERSION_NUM\"/" src/basic_memory/__init__.py
rm -f src/basic_memory/__init__.py.bak
# Commit version update
git add src/basic_memory/__init__.py
git commit -m "chore: update version to $VERSION_NUM for {{version}} release"
# Create and push tag
echo "🏷️ Creating tag {{version}}..."
git tag "{{version}}"
echo "📤 Pushing to GitHub..."
git push origin main
git push origin "{{version}}"
echo "✅ Release {{version}} created successfully!"
echo "📦 GitHub Actions will build and publish to PyPI"
echo "🔗 Monitor at: https://github.com/basicmachines-co/basic-memory/actions"
# Create a beta release (e.g., just beta v0.13.2b1)
beta version:
#!/usr/bin/env bash
set -euo pipefail
# Validate version format (allow beta/rc suffixes)
if [[ ! "{{version}}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(b[0-9]+|rc[0-9]+)$ ]]; then
echo "❌ Invalid beta version format. Use: v0.13.2b1 or v0.13.2rc1"
exit 1
fi
# Extract version number without 'v' prefix
VERSION_NUM=$(echo "{{version}}" | sed 's/^v//')
echo "🧪 Creating beta release {{version}}"
# Pre-flight checks
echo "📋 Running pre-flight checks..."
if [[ -n $(git status --porcelain) ]]; then
echo "❌ Uncommitted changes found. Please commit or stash them first."
exit 1
fi
if [[ $(git branch --show-current) != "main" ]]; then
echo "❌ Not on main branch. Switch to main first."
exit 1
fi
# Check if tag already exists
if git tag -l "{{version}}" | grep -q "{{version}}"; then
echo "❌ Tag {{version}} already exists"
exit 1
fi
# Run quality checks
echo "🔍 Running quality checks..."
just check
# Update version in __init__.py
echo "📝 Updating version in __init__.py..."
sed -i.bak "s/__version__ = \".*\"/__version__ = \"$VERSION_NUM\"/" src/basic_memory/__init__.py
rm -f src/basic_memory/__init__.py.bak
# Commit version update
git add src/basic_memory/__init__.py
git commit -m "chore: update version to $VERSION_NUM for {{version}} beta release"
# Create and push tag
echo "🏷️ Creating tag {{version}}..."
git tag "{{version}}"
echo "📤 Pushing to GitHub..."
git push origin main
git push origin "{{version}}"
echo "✅ Beta release {{version}} created successfully!"
echo "📦 GitHub Actions will build and publish to PyPI as pre-release"
echo "🔗 Monitor at: https://github.com/basicmachines-co/basic-memory/actions"
echo "📥 Install with: uv tool install basic-memory --pre"
# List all available recipes
default:
@just --list
+128
View File
@@ -0,0 +1,128 @@
# 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"}
+47 -25
View File
@@ -1,6 +1,6 @@
[project]
name = "basic-memory"
version = "0.10.0"
dynamic = ["version"]
description = "Local-first knowledge management combining Zettelkasten with knowledge graphs"
readme = "README.md"
requires-python = ">=3.12.1"
@@ -28,8 +28,13 @@ dependencies = [
"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.10.2",
"pyjwt>=2.10.1",
"python-dotenv>=1.1.0",
"pytest-aio>=1.9.0",
"aiofiles>=24.1.0", # Async file I/O
]
@@ -40,14 +45,15 @@ 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"]
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"
addopts = "--cov=basic_memory --cov-report term-missing"
testpaths = ["tests"]
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "function"
@@ -64,16 +70,20 @@ dev-dependencies = [
"pytest-cov>=4.1.0",
"pytest-mock>=3.12.0",
"pytest-asyncio>=0.24.0",
"pytest-xdist>=3.0.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",
"freezegun>=1.5.5",
]
[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__"]
@@ -84,23 +94,35 @@ reportMissingTypeStubs = false
pythonVersion = "3.12"
[tool.semantic_release]
version_variables = [
"src/basic_memory/__init__.py:__version__",
]
version_toml = [
"pyproject.toml:project.version",
]
major_on_zero = false
branch = "main"
changelog_file = "CHANGELOG.md"
build_command = "pip install uv && uv build"
dist_path = "dist/"
upload_to_pypi = true
commit_message = "chore(release): {version} [skip ci]"
[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
"*/mcp/tools/sync_status.py", # Covered by integration tests
"*/services/migration_service.py", # Complex migration scenarios
]
[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."
@@ -0,0 +1,156 @@
---
title: 'SPEC-1: Specification-Driven Development Process'
type: spec
permalink: specs/spec-1-specification-driven-development-process
tags:
- process
- specification
- development
- meta
---
# SPEC-1: Specification-Driven Development Process
## Why
We're implementing specification-driven development to solve the complexity and circular refactoring issues in our web development process.
Instead of getting lost in framework details and type gymnastics, we start with clear specifications that drive implementation.
The default approach of adhoc development with AI agents tends to result in:
- Circular refactoring cycles
- Fighting framework complexity
- Lost context between sessions
- Unclear requirements and scope
## What
This spec defines our process for using basic-memory as the specification engine to build basic-memory-cloud.
We're creating a recursive development pattern where basic-memory manages the specs that drive the development of basic-memory-cloud.
**Affected Areas:**
- All future component development
- Architecture decisions
- Agent collaboration workflows
- Knowledge management and context preservation
## How (High Level)
### Specification Structure
Name: Spec names should be numbered sequentially, followed by a description eg. `SPEC-X - Simple Description.md`.
See: [[Spec-2: Slash Commands Reference]]
Every spec is a complete thought containing:
- **Why**: The reasoning and problem being solved
- **What**: What is affected or changed
- **How**: High-level approach to implementation
- **How to Evaluate**: Testing/validation procedure
- Additional context as needed
### Living Specification Format
Specifications are **living documents** that evolve throughout implementation:
**Progress Tracking:**
- **Completed items**: Use ✅ checkmark emoji for implemented features
- **Pending items**: Use `- [ ]` GitHub-style checkboxes for remaining tasks
- **In-progress items**: Use `- [x]` when work is actively underway
**Status Philosophy:**
- **Avoid static status headers** like "COMPLETE" or "IN PROGRESS" that become stale
- **Use checklists within content** to show granular implementation progress
- **Keep specs informative** while providing clear progress visibility
- **Update continuously** as understanding and implementation evolve
**Example Format:**
```markdown
### ComponentName
- ✅ Basic functionality implemented
- ✅ Props and events defined
- - [ ] Add sorting controls
- - [ ] Improve accessibility
- - [x] Currently implementing responsive design
```
This creates **git-friendly progress tracking** where `[ ]` easily becomes `[x]` or ✅ when completed, and specs remain valuable throughout the development lifecycle.
## Claude Code
We will leverage Claude Code capabilities to make the process semi-automated.
- Slash commands: define repeatable steps in the process (create spec, implement, review, etc)
- Agents: define roles to carry out instructions (front end developer, baskend developer, etc)
- MCP tools: enable agents to implement specs via actions (write code, test, etc)
### Workflow
1. **Create**: Write spec as complete thought in `/specs` folder
2. **Discuss**: Iterate and refine through agent collaboration
3. **Implement**: Hand spec to appropriate specialist agent
4. **Validate**: Review implementation against spec criteria
5. **Document**: Update spec with learnings and decisions
### Slash Commands
Claude slash commands are used to manage the flow.
These are simple instructions to help make the process uniform.
They can be updated and refined as needed.
- `/spec create [name]` - Create new specification
- `/spec status` - Show current spec states
- `/spec implement [name]` - Hand to appropriate agent
- `/spec review [name]` - Validate implementation
### Agent Orchestration
Agents are defined with clear roles, for instance:
- **system-architect**: Creates high-level specs, ADRs, architectural decisions
- **vue-developer**: Component specs, UI patterns, frontend architecture
- **python-developer**: Implementation specs, technical details, backend logic
-
- Each agent reads/updates specs through basic-memory tools.
## How to Evaluate
### Success Criteria
- Specs provide clear, actionable guidance for implementation
- Reduced circular refactoring and scope creep
- Persistent context across development sessions
- Clean separation between "what/why" and implementation details
- Specs record a history of what happened and why for historical context
### Testing Procedure
1. Create a spec for an existing problematic component
2. Have an agent implement following only the spec
3. Compare result quality and development speed vs. ad-hoc approach
4. Measure context preservation across sessions
5. Evaluate spec clarity and completeness
### Metrics
- Time from spec to working implementation
- Number of refactoring cycles required
- Agent understanding of requirements
- Spec reusability for similar components
## Notes
- Start simple: specs are just complete thoughts, not heavy processes
- Use basic-memory's knowledge graph to link specs, decisions, components
- Let the process evolve naturally based on what works
- Focus on solving the actual problem: Manage complexity in development
## Observations
- [problem] Web development without clear goals and documentation circular refactoring cycles #complexity
- [solution] Specification-driven development reduces scope creep and context loss #process-improvement
- [pattern] basic-memory as specification engine creates recursive development loop #meta-development
- [workflow] Five-step process: Create → Discuss → Implement → Validate → Document #methodology
- [tool] Slash commands provide uniform process automation #automation
- [agent-pattern] Three specialized agents handle different implementation domains #specialization
- [success-metric] Time from spec to working implementation measures process efficiency #measurement
- [learning] Process should evolve naturally based on what works in practice #adaptation
- [format] Living specifications use checklists for progress tracking instead of static status headers #documentation
- [evolution] Specs evolve throughout implementation maintaining value as working documents #continuous-improvement
## Relations
- spec [[Spec-2: Slash Commands Reference]]
- spec [[Spec-3: Agent Definitions]]
@@ -0,0 +1,245 @@
---
title: 'SPEC-11: Basic Memory API Performance Optimization'
type: spec
permalink: specs/spec-11-basic-memory-api-performance-optimization
tags:
- performance
- api
- mcp
- database
- cloud
---
# SPEC-11: Basic Memory API Performance Optimization
## Why
The Basic Memory API experiences significant performance issues in cloud environments due to expensive per-request initialization. MCP tools making
HTTP requests to the API suffer from 350ms-2.6s latency overhead **before** any actual operation occurs.
**Root Cause Analysis:**
- GitHub Issue #82 shows repeated initialization sequences in logs (16:29:35 and 16:49:58)
- Each MCP tool call triggers full database initialization + project reconciliation
- `get_engine_factory()` dependency calls `db.get_or_create_db()` on every request
- `reconcile_projects_with_config()` runs expensive sync operations repeatedly
**Performance Impact:**
- Database connection setup: ~50-100ms per request
- Migration checks: ~100-500ms per request
- Project reconciliation: ~200ms-2s per request
- **Total overhead**: ~350ms-2.6s per MCP tool call
This creates compounding effects with tenant auto-start delays and increases timeout risk in cloud deployments.
Github issue: https://github.com/basicmachines-co/basic-memory-cloud/issues/82
## What
This optimization affects the **core basic-memory repository** components:
1. **API Lifespan Management** (`src/basic_memory/api/app.py`)
- Cache database connections in app state during startup
- Avoid repeated expensive initialization
2. **Dependency Injection** (`src/basic_memory/deps.py`)
- Modify `get_engine_factory()` to use cached connections
- Eliminate per-request database setup
3. **Initialization Service** (`src/basic_memory/services/initialization.py`)
- Add caching/throttling to project reconciliation
- Skip expensive operations when appropriate
4. **Configuration** (`src/basic_memory/config.py`)
- Add optional performance flags for cloud environments
**Backwards Compatibility**: All changes must be backwards compatible with existing CLI and non-cloud usage.
## How (High Level)
### Phase 1: Cache Database Connections (Critical - 80% of gains)
**Problem**: `get_engine_factory()` calls `db.get_or_create_db()` per request
**Solution**: Cache database engine/session in app state during lifespan
1. **Modify API Lifespan** (`api/app.py`):
```python
@asynccontextmanager
async def lifespan(app: FastAPI):
app_config = ConfigManager().config
await initialize_app(app_config)
# Cache database connection in app state
engine, session_maker = await db.get_or_create_db(app_config.database_path)
app.state.engine = engine
app.state.session_maker = session_maker
# ... rest of startup logic
```
2. Modify Dependency Injection (deps.py):
```python
async def get_engine_factory(
request: Request
) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]:
"""Get cached engine and session maker from app state."""
return request.app.state.engine, request.app.state.session_maker
```
Phase 2: Optimize Project Reconciliation (Secondary - 20% of gains)
Problem: reconcile_projects_with_config() runs expensive sync repeatedly
Solution: Add module-level caching with time-based throttling
1. Add Reconciliation Cache (services/initialization.py):
```ptyhon
_project_reconciliation_completed = False
_last_reconciliation_time = 0
async def reconcile_projects_with_config(app_config, force=False):
# Skip if recently completed (within 60 seconds) unless forced
if recently_completed and not force:
return
# ... existing logic
```
Phase 3: Cloud Environment Flags (Optional)
Problem: Force expensive initialization in production environments
Solution: Add skip flags for cloud/stateless deployments
1. Add Config Flag (config.py):
skip_initialization_sync: bool = Field(default=False)
2. Configure in Cloud (basic-memory-cloud integration):
BASIC_MEMORY_SKIP_INITIALIZATION_SYNC=true
How to Evaluate
Success Criteria
1. Performance Metrics (Primary):
- MCP tool response time reduced by 50%+ (measure before/after)
- Database connection overhead eliminated (0ms vs 50-100ms)
- Migration check overhead eliminated (0ms vs 100-500ms)
- Project reconciliation overhead reduced by 90%+
2. Load Testing:
- Concurrent MCP tool calls maintain performance
- No memory leaks in cached connections
- Database connection pool behaves correctly
3. Functional Correctness:
- All existing API endpoints work identically
- MCP tools maintain full functionality
- CLI operations unaffected
- Database migrations still execute properly
4. Backwards Compatibility:
- No breaking changes to existing APIs
- Config changes are optional with safe defaults
- Non-cloud deployments work unchanged
Testing Strategy
Performance Testing:
# Before optimization
time basic-memory-mcp-tools write_note "test" "content" "folder"
# Measure: ~1-3 seconds
# After optimization
time basic-memory-mcp-tools write_note "test" "content" "folder"
# Target: <500ms
Load Testing:
# Multiple concurrent MCP tool calls
for i in {1..10}; do
basic-memory-mcp-tools search "test" &
done
wait
# Verify: No degradation, consistent response times
Regression Testing:
# Full basic-memory test suite
just test
# All tests must pass
# Integration tests with cloud deployment
# Verify MCP gateway → API → database flow works
Validation Checklist
- Phase 1 Complete: Database connections cached, dependency injection optimized
- Performance Benchmark: 50%+ improvement in MCP tool response times
- Memory Usage: No leaks in cached connections over 24h+ periods
- Stress Testing: 100+ concurrent requests maintain performance
- Backwards Compatibility: All existing functionality preserved
- Documentation: Performance optimization documented in README
- Cloud Integration: basic-memory-cloud sees performance benefits
## Implementation Status ✅ COMPLETED
**Implementation Date**: 2025-09-26
**Branch**: `feature/spec-11-api-performance-optimization`
**Commit**: `771f60b`
### ✅ Phase 1: Database Connection Caching - IMPLEMENTED
**Files Modified:**
- `src/basic_memory/api/app.py` - Added database connection caching in app.state
- `src/basic_memory/deps.py` - Updated get_engine_factory() to use cached connections
- `src/basic_memory/config.py` - Added skip_initialization_sync configuration flag
**Implementation Details:**
1. **API Lifespan Caching**: Database engine and session_maker cached in app.state during startup
2. **Dependency Injection Optimization**: get_engine_factory() now returns cached connections instead of calling get_or_create_db()
3. **Project Reconciliation Removal**: Eliminated expensive reconcile_projects_with_config() from API startup
4. **CLI Fallback Preserved**: Non-API contexts continue to work with fallback database initialization
### ✅ Performance Validation - ACHIEVED
**Live Testing Results** (2025-09-26 14:03-14:09):
| Operation | Before | After | Improvement |
|-----------|--------|-------|-------------|
| `read_note` | 350ms-2.6s | **20ms** | **95-99% faster** |
| `edit_note` | 350ms-2.6s | **218ms** | **75-92% faster** |
| `search_notes` | 350ms-2.6s | **<500ms** | **Responsive** |
| `list_memory_projects` | N/A | **<100ms** | **Fast** |
**Key Achievements:**
-**95-99% improvement** in read operations (primary workflow)
-**75-92% improvement** in edit operations
-**Zero overhead** for project switching
-**Database connection overhead eliminated** (0ms vs 50-100ms)
-**Project reconciliation delays removed** from API requests
-**<500ms target achieved** for all operations except write (which includes file sync)
### ✅ Backwards Compatibility - MAINTAINED
- All existing functionality preserved
- CLI operations unaffected
- Fallback for non-API contexts maintained
- No breaking changes to existing APIs
- Optional configuration with safe defaults
### ✅ Testing Validation - PASSED
- Integration tests passing
- Type checking clear
- Linting checks passed
- Live testing with real MCP tools successful
- Multi-project workflows validated
- Rapid project switching validated
## Notes
Implementation Priority:
- ✅ Phase 1 COMPLETED: Database connection caching provides 95%+ performance gains
- ⚪ Phase 2 NOT NEEDED: Project reconciliation removal achieved the goals
- ⚪ Phase 3 INCLUDED: skip_initialization_sync flag added
Risk Mitigation:
- ✅ All changes backwards compatible implemented
- ✅ Gradual implementation successful (Phase 1 → validation)
- ✅ Easy rollback via configuration flags available
Cloud Integration:
- ✅ This optimization directly addresses basic-memory-cloud issue #82
- ✅ Changes in core basic-memory will benefit all cloud tenants
- ✅ No changes needed in basic-memory-cloud itself
**Result**: SPEC-11 performance optimizations successfully implemented and validated. The 95-99% improvement in MCP tool response times exceeds the original 50-80% target, providing exceptional performance gains for cloud deployments and local usage.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,210 @@
---
title: 'SPEC-14: Cloud Git Versioning & GitHub Backup'
type: spec
permalink: specs/spec-14-cloud-git-versioning
tags:
- git
- github
- backup
- versioning
- cloud
related:
- specs/spec-9-multi-project-bisync
- specs/spec-9-follow-ups-conflict-sync-and-observability
status: deferred
---
# SPEC-14: Cloud Git Versioning & GitHub Backup
**Status: DEFERRED** - Postponed until multi-user/teams feature development. Using S3 versioning (SPEC-9.1) for v1 instead.
## Why Deferred
**Original goals can be met with simpler solutions:**
- Version history → **S3 bucket versioning** (automatic, zero config)
- Offsite backup → **Tigris global replication** (built-in)
- Restore capability → **S3 version restore** (`bm cloud restore --version-id`)
- Collaboration → **Deferred to teams/multi-user feature** (not v1 requirement)
**Complexity vs value trade-off:**
- Git integration adds: committer service, puller service, webhooks, LFS, merge conflicts
- Risk: Loop detection between Git ↔ rclone bisync ↔ local edits
- S3 versioning gives 80% of value with 5% of complexity
**When to revisit:**
- Teams/multi-user features (PR-based collaboration workflow)
- User requests for commit messages and branch-based workflows
- Need for fine-grained audit trail beyond S3 object metadata
---
## Original Specification (for reference)
## Why
Early access users want **transparent version history**, easy **offsite backup**, and a familiar **restore/branching** workflow. Git/GitHub integration would provide:
- Auditable history of every change (who/when/why)
- Branches/PRs for review and collaboration
- Offsite private backup under the user's control
- Escape hatch: users can always `git clone` their knowledge base
**Note:** These goals are now addressed via S3 versioning (SPEC-9.1) for single-user use case.
## Goals
- **Transparent**: Users keep using Basic Memory; Git runs behind the scenes.
- **Private**: Push to a **private GitHub repo** that the user owns (or tenant org).
- **Reliable**: No data loss, deterministic mapping of filesystem ↔ Git.
- **Composable**: Plays nicely with SPEC9 bisync and upcoming conflict features (SPEC9 FollowUps).
**NonGoals (for v1):**
- Finegrained perfile encryption in Git history (can be layered later).
- Large media optimization beyond Git LFS defaults.
## User Stories
1. *As a user*, I connect my GitHub and choose a private backup repo.
2. *As a user*, every change I make in cloud (or via bisync) is **committed** and **pushed** automatically.
3. *As a user*, I can **restore** a file/folder/project to a prior version.
4. *As a power user*, I can **git pull/push** directly to collaborate outside the app.
5. *As an admin*, I can enforce repo ownership (tenant org) and leastprivilege scopes.
## Scope
- **In scope:** Full repo backup of `/app/data/` (all projects) with optional selective subpaths.
- **Out of scope (v1):** Partial shallow mirrors; encrypted Git; crossprovider SCM (GitLab/Bitbucket).
## Architecture
### Topology
- **Authoritative working tree**: `/app/data/` (bucket mount) remains the source of truth (SPEC9).
- **Bare repo** lives alongside: `/app/git/${tenant}/knowledge.git` (serverside).
- **Mirror remote**: `github.com/<owner>/<repo>.git` (private).
```mermaid
flowchart LR
A[/Users & Agents/] -->|writes/edits| B[/app/data/]
B -->|file events| C[Committer Service]
C -->|git commit| D[(Bare Repo)]
D -->|push| E[(GitHub Private Repo)]
E -->|webhook (push)| F[Puller Service]
F -->|git pull/merge| D
D -->|checkout/merge| B
```
### Services
- **Committer Service** (daemon):
- Watches `/app/data/` for changes (inotify/poll)
- Batches changes (debounce e.g. 25s)
- Writes `.bmmeta` (if present) into commit message trailer (see FollowUps)
- `git add -A && git commit -m "chore(sync): <summary>
BM-Meta: <json>"`
- Periodic `git push` to GitHub mirror (configurable interval)
- **Puller Service** (webhook target):
- Receives GitHub webhook (push) → `git fetch`
- **Fastforward** merges to `main` only; reject nonFF unless policy allows
- Applies changes back to `/app/data/` via clean checkout
- Emits sync events for Basic Memory indexers
### Auth & Security
- **GitHub App** (recommended): minimal scopes: `contents:read/write`, `metadata:read`, webhook.
- Tenantscoped installation; repo created in user account or tenant org.
- Tokens stored in KMS/secret manager; rotated automatically.
- Optional policy: allow only **FF merges** on `main`; nonFF requires PR.
### Repo Layout
- **Monorepo** (default): one repo per tenant mirrors `/app/data/` with subfolders per project.
- Optional multirepo mode (later): one repo per project.
### File Handling
- Honor `.gitignore` generated from `.bmignore.rclone` + BM defaults (cache, temp, state).
- **Git LFS** for large binaries (images, media) — auto track by extension/size threshold.
- Normalize newline + Unicode (aligns with FollowUps).
### Conflict Model
- **Primary concurrency**: SPEC9 FollowUps (`.bmmeta`, conflict copies) stays the first line of defense.
- **Git merges** are a **secondary** mechanism:
- Server only automerges **text** conflicts when trivial (FF or clean 3way).
- Otherwise, create `name (conflict from <branch>, <ts>).md` and surface via events.
### Data Flow vs Bisync
- Bisync (rclone) continues between local sync dir ↔ bucket.
- Git sits **cloudside** between bucket and GitHub.
- On **pull** from GitHub → files written to `/app/data/` → picked up by indexers & eventually by bisync back to users.
## CLI & UX
New commands (cloud mode):
- `bm cloud git connect` — Launch GitHub App installation; create private repo; store installation id.
- `bm cloud git status` — Show connected repo, last push time, last webhook delivery, pending commits.
- `bm cloud git push` — Manual push (rarely needed).
- `bm cloud git pull` — Manual pull/FF (admin only by default).
- `bm cloud snapshot -m "message"` — Create a tagged pointintime snapshot (git tag).
- `bm restore <path> --to <commit|tag>` — Restore file/folder/project to prior version.
Settings:
- `bm config set git.autoPushInterval=5s`
- `bm config set git.lfs.sizeThreshold=10MB`
- `bm config set git.allowNonFF=false`
## Migration & Backfill
- On connect, if repo empty: initial commit of entire `/app/data/`.
- If repo has content: require **onetime import** path (clone to staging, reconcile, choose direction).
## Edge Cases
- Massive deletes: gated by SPEC9 `max_delete` **and** Git prepush hook checks.
- Case changes and rename detection: rely on git rename heuristics + FollowUps move hints.
- Secrets: default ignore common secret patterns; allow custom deny list.
## Telemetry & Observability
- Emit `git_commit`, `git_push`, `git_pull`, `git_conflict` events with correlation IDs.
- `bm sync --report` extended with Git stats (commit count, delta bytes, push latency).
## Phased Plan
### Phase 0 — Prototype (1 sprint)
- Server: bare repo init + simple committer (batch every 10s) + manual GitHub token.
- CLI: `bm cloud git connect --token <PAT>` (devonly)
- Success: edits in `/app/data/` appear in GitHub within 30s.
### Phase 1 — GitHub App & Webhooks (12 sprints)
- Switch to GitHub App installs; create private repo; store installation id.
- Committer hardened (debounce 25s, backoff, retries).
- Puller service with webhook → FF merge → checkout to `/app/data/`.
- LFS autotrack + `.gitignore` generation.
- CLI surfaces status + logs.
### Phase 2 — Restore & Snapshots (1 sprint)
- `bm restore` for file/folder/project with dryrun.
- `bm cloud snapshot` tags + list/inspect.
- Policy: PRonly nonFF, admin override.
### Phase 3 — Selective & MultiRepo (nicetohave)
- Include/exclude projects; optional perproject repos.
- Advanced policies (branch protections, required reviews).
## Acceptance Criteria
- Changes to `/app/data/` are committed and pushed automatically within configurable interval (default ≤5s).
- GitHub webhook pull results in updated files in `/app/data/` (FFonly by default).
- LFS configured and functioning; large files don't bloat history.
- `bm cloud git status` shows connected repo and last push/pull times.
- `bm restore` restores a file/folder to a prior commit with a clear audit trail.
- Endtoend works alongside SPEC9 bisync without loops or data loss.
## Risks & Mitigations
- **Loop risk (Git ↔ Bisync)**: Writes to `/app/data/` → bisync → local → user edits → back again. *Mitigation*: Debounce, commit squashing, idempotent `.bmmeta` versioning, and watch exclusion windows during pull.
- **Repo bloat**: Lots of binary churn. *Mitigation*: default LFS, size threshold, optional mediaonly repo later.
- **Security**: Token leakage. *Mitigation*: GitHub App with shortlived tokens, KMS storage, scoped permissions.
- **Merge complexity**: Nontrivial conflicts. *Mitigation*: prefer FF; otherwise conflict copies + events; require PR for nonFF.
## Open Questions
- Do we default to **monorepo** per tenant, or offer projectperrepo at connect time?
- Should `restore` write to a branch and open a PR, or directly modify `main`?
- How do we expose Git history in UI (timeline view) without users dropping to CLI?
## Appendix: Sample Config
```json
{
"git": {
"enabled": true,
"repo": "https://github.com/<owner>/<repo>.git",
"autoPushInterval": "5s",
"allowNonFF": false,
"lfs": { "sizeThreshold": 10485760 }
}
}
```
+120
View File
@@ -0,0 +1,120 @@
---
title: 'SPEC-2: Slash Commands Reference'
type: spec
permalink: specs/spec-2-slash-commands-reference
tags:
- commands
- process
- reference
---
# SPEC-2: Slash Commands Reference
This document defines the slash commands used in our specification-driven development process.
## /spec create [name]
**Purpose**: Create a new specification document
**Usage**: `/spec create notes-decomposition`
**Process**:
1. Create new spec document in `/specs` folder
2. Use SPEC-XXX numbering format (auto-increment)
3. Include standard spec template:
- Why (reasoning/problem)
- What (affected areas)
- How (high-level approach)
- How to Evaluate (testing/validation)
4. Tag appropriately for knowledge graph
5. Link to related specs/components
**Template**:
```markdown
# SPEC-XXX: [Title]
## Why
[Problem statement and reasoning]
## What
[What is affected or changed]
## How (High Level)
[Approach to implementation]
## How to Evaluate
[Testing/validation procedure]
## Notes
[Additional context as needed]
```
## /spec status
**Purpose**: Show current status of all specifications
**Usage**: `/spec status`
**Process**:
1. Search all specs in `/specs` folder
2. Display table showing:
- Spec number and title
- Status (draft, approved, implementing, complete)
- Assigned agent (if any)
- Last updated
- Dependencies
## /spec implement [name]
**Purpose**: Hand specification to appropriate agent for implementation
**Usage**: `/spec implement SPEC-002`
**Process**:
1. Read the specified spec
2. Analyze requirements to determine appropriate agent:
- Frontend components → vue-developer
- Architecture/system design → system-architect
- Backend/API → python-developer
3. Launch agent with spec context
4. Agent creates implementation plan
5. Update spec with implementation status
## /spec review [name]
**Purpose**: Review implementation against specification criteria
**Usage**: `/spec review SPEC-002`
**Process**:
1. Read original spec and "How to Evaluate" section
2. Examine current implementation
3. Test against success criteria
4. Document gaps or issues
5. Update spec with review results
6. Recommend next actions (complete, revise, iterate)
## Command Extensions
As the process evolves, we may add:
- `/spec link [spec1] [spec2]` - Create dependency links
- `/spec archive [name]` - Archive completed specs
- `/spec template [type]` - Create spec from template
- `/spec search [query]` - Search spec content
## References
- Claude Slash commands: https://docs.anthropic.com/en/docs/claude-code/slash-commands
## Creating a command
Commands are implemented as Claude slash commands:
Location in repo: .claude/commands/
In the following example, we create the /optimize command:
```bash
# Create a project command
mkdir -p .claude/commands
echo "Analyze this code for performance issues and suggest optimizations:" > .claude/commands/optimize.md
```
+108
View File
@@ -0,0 +1,108 @@
---
title: 'SPEC-3: Agent Definitions'
type: spec
permalink: specs/spec-3-agent-definitions
tags:
- agents
- roles
- process
---
# SPEC-3: Agent Definitions
This document defines the specialist agents used in our specification-driven development process.
## system-architect
**Role**: High-level system design and architectural decisions
**Responsibilities**:
- Create architectural specifications and ADRs
- Analyze system-wide impacts and trade-offs
- Design component interfaces and data flow
- Evaluate technical approaches and patterns
- Document architectural decisions and rationale
**Expertise Areas**:
- System architecture and design patterns
- Technology evaluation and selection
- Scalability and performance considerations
- Integration patterns and API design
- Technical debt and refactoring strategies
**Typical Specs**:
- System architecture overviews
- Component decomposition strategies
- Data flow and state management
- Integration and deployment patterns
## vue-developer
**Role**: Frontend component development and UI implementation
**Responsibilities**:
- Create Vue.js component specifications
- Implement responsive UI components
- Design component APIs and interfaces
- Optimize for performance and accessibility
- Document component usage and patterns
**Expertise Areas**:
- Vue.js 3 Composition API
- Nuxt 3 framework patterns
- shadcn-vue component library
- Responsive design and CSS
- TypeScript integration
- State management with Pinia
**Typical Specs**:
- Individual component specifications
- UI pattern libraries
- Responsive design approaches
- Component interaction flows
## python-developer
**Role**: Backend development and API implementation
**Responsibilities**:
- Create backend service specifications
- Implement APIs and data processing
- Design database schemas and queries
- Optimize performance and reliability
- Document service interfaces and behavior
**Expertise Areas**:
- FastAPI and Python web frameworks
- Database design and operations
- API design and documentation
- Authentication and security
- Performance optimization
- Testing and validation
**Typical Specs**:
- API endpoint specifications
- Database schema designs
- Service integration patterns
- Performance optimization strategies
## Agent Collaboration Patterns
### Handoff Protocol
1. Agent receives spec through `/spec implement [name]`
2. Agent reviews spec and creates implementation plan
3. Agent documents progress and decisions in spec
4. Agent hands off to another agent if cross-domain work needed
5. Final agent updates spec with completion status
### Communication Standards
- All agents update specs through basic-memory MCP tools
- Document decisions and trade-offs in spec notes
- Link related specs and components
- Preserve context for future reference
### Quality Standards
- Follow existing codebase patterns and conventions
- Write tests that validate spec requirements
- Document implementation choices
- Consider maintainability and extensibility
+201
View File
@@ -0,0 +1,201 @@
---
title: 'SPEC-5: CLI Cloud Upload via WebDAV'
type: spec
permalink: specs/spec-5-cli-cloud-upload-via-webdav
tags:
- cli
- webdav
- upload
- migration
- poc
---
# SPEC-5: CLI Cloud Upload via WebDAV
## Why
Existing basic-memory users need a simple migration path to basic-memory-cloud. The web UI drag-and-drop approach outlined in GitHub issue #59, while user-friendly, introduces significant complexity for a proof-of-concept:
- Complex web UI components for file upload and progress tracking
- Browser file handling limitations and CORS complexity
- Proxy routing overhead for large file transfers
- Authentication integration across multiple services
A CLI-first approach solves these issues by:
- **Leveraging existing infrastructure**: Both cloud CLI and tenant API already exist with WorkOS JWT authentication
- **Familiar user experience**: Basic-memory users are CLI-comfortable and expect command-line tools
- **Direct connection efficiency**: Bypassing the MCP gateway/proxy for bulk file transfers
- **Rapid implementation**: Building on existing `CLIAuth` and FastAPI foundations
The fundamental problem is migration friction - users have local basic-memory projects but no path to cloud tenants. A simple CLI upload command removes this barrier immediately.
## What
This spec defines a CLI-based project upload system using WebDAV for direct tenant connections.
**Affected Areas:**
- `apps/cloud/src/basic_memory_cloud/cli/main.py` - Add upload command to existing CLI
- `apps/api/src/basic_memory_cloud_api/main.py` - Add WebDAV endpoints to tenant FastAPI
- Authentication flow - Reuse existing WorkOS JWT validation
- File transfer protocol - WebDAV for cross-platform compatibility
**Core Components:**
### CLI Upload Command
```bash
basic-memory-cloud upload <project-path> --tenant-url https://basic-memory-{tenant}.fly.dev
```
### WebDAV Server Endpoints
- `GET/PUT/DELETE /webdav/*` - Standard WebDAV operations on tenant file system
- Authentication via existing JWT validation
- File operations preserve timestamps and directory structure
### Authentication Flow
```
1. User runs `basic-memory-cloud login` (existing)
2. CLI stores WorkOS JWT token (existing)
3. Upload command reads JWT from storage
4. WebDAV requests include JWT in Authorization header
5. Tenant API validates JWT using existing middleware
```
## How (High Level)
### Implementation Strategy
**Phase 1: CLI Command**
- Add `upload` command to existing Typer app
- Reuse `CLIAuth` class for token management
- Implement WebDAV client using `webdavclient3` or similar
- Rich progress bars for transfer feedback
**Phase 2: WebDAV Server**
- Add WebDAV endpoints to existing tenant FastAPI app
- Leverage existing `get_current_user` dependency for authentication
- Map WebDAV operations to tenant file system
- Preserve file modification times using `os.utime()`
**Phase 3: Integration**
- Direct connection bypasses MCP gateway and proxy
- Simple conflict resolution: overwrite existing files
- Error handling: fail fast with clear error messages
### Technical Architecture
```
basic-memory-cloud CLI → WorkOS JWT → Direct WebDAV → Tenant FastAPI
Tenant File System
```
**Key Libraries:**
- CLI: `webdavclient3` for WebDAV client operations
- API: `wsgidav` or FastAPI-compatible WebDAV server
- Progress: `rich` library (already imported in CLI)
- Auth: Existing WorkOS JWT infrastructure
### WebDAV Protocol Choice
WebDAV provides:
- **Cross-platform clients**: Native support in most operating systems
- **Standardized protocol**: Well-defined for file operations
- **HTTP-based**: Works with existing FastAPI and JWT auth
- **Library support**: Good Python libraries for both client and server
### POC Constraints
**Simplifications for rapid implementation:**
- **Known tenant URLs**: Assume `https://basic-memory-{tenant}.fly.dev` format
- **Upload only**: No download or bidirectional sync
- **Overwrite conflicts**: No merge or conflict resolution prompting
- **No fallbacks**: Fail fast if WebDAV connection issues occur
- **Direct connection only**: No proxy fallback mechanism
## How to Evaluate
### Success Criteria
**Functional Requirements:**
- [ ] Transfer complete basic-memory project (100+ files) in < 30 seconds
- [ ] Preserve directory structure exactly as in source project
- [ ] Preserve file modification timestamps for proper sync behavior
- [ ] Rich progress bars show real-time transfer status (files/MB transferred)
- [ ] WorkOS JWT authentication validates correctly on WebDAV endpoints
- [ ] Direct tenant connection bypasses MCP gateway successfully
**Quality Requirements:**
- [ ] Clear error messages for authentication failures
- [ ] Graceful handling of network interruptions
- [ ] CLI follows existing command patterns and help text standards
- [ ] WebDAV endpoints integrate cleanly with existing FastAPI app
**Performance Requirements:**
- [ ] File transfer speed > 1MB/s on typical connections
- [ ] Memory usage remains reasonable for large projects
- [ ] No timeout issues with 500+ file projects
### Testing Procedure
**Unit Testing:**
1. CLI command parsing and argument validation
2. WebDAV client connection and authentication
3. File timestamp preservation during transfer
4. JWT token validation on WebDAV endpoints
**Integration Testing:**
1. End-to-end upload of test project
2. Direct tenant connection without proxy
3. File integrity verification after upload
4. Progress tracking accuracy during transfer
**User Experience Testing:**
1. Upload existing basic-memory project from local installation
2. Verify uploaded files appear correctly in cloud tenant
3. Confirm basic-memory database rebuilds properly with uploaded files
4. Test CLI help text and error message clarity
### Validation Commands
**Setup:**
```bash
# Login to WorkOS
basic-memory-cloud login
# Upload project
basic-memory-cloud upload ~/my-notes --tenant-url https://basic-memory-test.fly.dev
```
**Verification:**
```bash
# Check tenant health and file count via API
curl -H "Authorization: Bearer $JWT" https://basic-memory-test.fly.dev/health
curl -H "Authorization: Bearer $JWT" https://basic-memory-test.fly.dev/notes/search
```
### Performance Benchmarks
**Target metrics for 100MB basic-memory project:**
- Transfer time: < 30 seconds
- Memory usage: < 100MB during transfer
- Progress updates: Every 1MB or 10 files
- Authentication time: < 2 seconds
## Observations
- [implementation-speed] CLI approach significantly faster than web UI for POC development #rapid-prototyping
- [user-experience] Basic-memory users already comfortable with CLI tools #user-familiarity
- [architecture-benefit] Direct connection eliminates proxy complexity and latency #performance
- [auth-reuse] Existing WorkOS JWT infrastructure handles authentication cleanly #code-reuse
- [webdav-choice] WebDAV protocol provides cross-platform compatibility and standard libraries #protocol-selection
- [poc-scope] Simple conflict handling and error recovery sufficient for proof-of-concept #scope-management
- [migration-value] Removes primary barrier for local users migrating to cloud platform #business-value
## Relations
- depends_on [[SPEC-1: Specification-Driven Development Process]]
- enables [[GitHub Issue #59: Web UI Upload Feature]]
- uses [[WorkOS Authentication Integration]]
- builds_on [[Existing Cloud CLI Infrastructure]]
- builds_on [[Existing Tenant API Architecture]]
@@ -0,0 +1,486 @@
---
title: 'SPEC-6: Explicit Project Parameter Architecture'
type: spec
permalink: specs/spec-6-explicit-project-parameter-architecture
tags:
- architecture
- mcp
- project-management
- stateless
---
# SPEC-6: Explicit Project Parameter Architecture
## Why
The current session-based project management system has critical reliability issues:
1. **Session State Fragility**: Claude iOS mobile client fails to maintain consistent session IDs across MCP tool calls, causing project switching to silently fail (Issue #74)
2. **Scaling Limitations**: Redis-backed session state creates single-point-of-failure and prevents horizontal scaling
3. **Client Compatibility**: Session tracking works inconsistently across different MCP clients (web, mobile, API)
4. **Hidden Complexity**: Users cannot see or understand "current project" state, leading to confusion when operations execute in wrong projects
5. **Silent Failures**: Operations appear successful but execute in unintended projects, risking data integrity
Evidence from production logs shows each MCP tool call from mobile client receives different session IDs:
```
create_memory_project: session_id=12cdfc24913b48f8b680ed4b2bfdb7ba
switch_project: session_id=050a69275d98498cbdd227cdb74d9740
list_directory: session_id=85f3483014af4136a5d435c76ded212f
```
Related Github issue: https://github.com/basicmachines-co/basic-memory-cloud/issues/75
## Status
**Current Status**: **Phase 1 Implementation Complete**
**Target**: Fix Claude iOS session ID consistency issues
**Draft PR**: https://github.com/basicmachines-co/basic-memory/pull/298
### 🎉 **MAJOR MILESTONE ACHIEVED**
The complete stateless architecture has been successfully implemented for Basic Memory's MCP server! This represents a **fundamental architectural improvement** that solves the Claude iOS compatibility issue while making the entire system more robust and predictable.
#### Implementation Summary:
- **16 files modified** with 582 additions and 550 deletions
- **All 17 MCP tools** converted to stateless architecture
- **147 tests updated** across 5 test files (100% passing)
- **Complete session state removal** from core MCP tools
- **Enhanced error handling** and security validations preserved
### Progress Summary
**Complete Stateless Architecture Implementation (All 17 tools)**
- Stateless `get_active_project()` function implemented and deployed
- All session state dependencies removed across entire MCP server
- All MCP tools require explicit `project` parameter as first argument
**Content Management Tools Complete (6/6 tools)**
- `write_note`, `read_note`, `delete_note`, `edit_note`
- `view_note`, `read_content`
**Knowledge Graph Navigation Tools Complete (3/3 tools)**
- `build_context`, `recent_activity`, `list_directory`
**Search & Discovery Tools Complete (1/1 tools)**
- `search_notes`
**Visualization Tools Complete (1/1 tools)**
- `canvas`
**Project Management Cleanup Complete**
- Removed `switch_project` and `get_current_project` tools ✅
- Updated `set_default_project` to remove activate parameter ✅
**Comprehensive Testing Complete (157 tests)**
- All test suites updated to use stateless architecture (147 existing tests)
- Single project constraint mode integration tests (10 new tests)
- 100% test pass rate across all tool test files
- Security validations preserved and working
- Error handling comprehensive and user-friendly
**Documentation & Examples Complete**
- All tool docstrings updated with stateless examples
- Project parameter usage clearly documented
- Error handling and security behavior documented
**Enhanced Discovery Mode Complete**
- `recent_activity` tool supports dual-mode operation (discovery vs project-specific)
- ProjectActivitySummary schema provides cross-project insights
- Recent activity prompt updated to support both modes
- Comprehensive project distribution statistics and most active project tracking
**Single Project Constraint Mode Complete**
- `--project` CLI parameter for MCP server constraint
- Environment variable control (`BASIC_MEMORY_MCP_PROJECT`)
- Automatic project override in `get_active_project()` function
- Project management tools disabled in constrained mode with helpful CLI guidance
- Comprehensive integration test suite (10 tests covering all constraint scenarios)
## What
Transform Basic Memory from stateful session-based to stateless explicit project parameter architecture:
### Core Changes
1. **Mandatory Project Parameter**: All MCP tools require explicit `project` parameter
2. **Remove Session State**: Eliminate Redis, session middleware, and `switch_project` tool
3. **Stateless HTTP**: Enable `stateless_http=True` for horizontal scaling
4. **Enhanced Context Discovery**: Improve `recent_activity` to show project distribution
5. **Clear Response Format**: All tool responses display target project information
Implementation Approach
- Each tool will directly accept the project parameter
- Remove all calls to context-based project retrieval
- Validate project exists before operations
- Clear error messages when project not found
- Backward compatibility: Initially keep optional parameter, then make required
### Affected MCP Tools
**Content Management** (require project parameter):
- `write_note(project, title, content, folder)`
- `read_note(project, identifier)`
- `edit_note(project, identifier, operation, content)`
- `delete_note(project, identifier)`
- `view_note(project, identifier)`
- `read_content(project, path)`
**Knowledge Graph Navigation** (require project parameter):
- `build_context(project, url, timeframe, depth, max_related)`
- `list_directory(project, dir_name, depth, file_name_glob)`
- `search_notes(project, query, search_type, types, entity_types)`
**Search & Discovery** (use project parameter for specific project or none for discovery):
- `recent_activity(project, timeframe, depth, max_related)`
**Visualization** (require project parameter):
- `canvas(project, nodes, edges, title, folder)`
**Project Management** (unchanged - already stateless):
- `list_memory_projects()`
- `create_memory_project(project_name, project_path, set_default)`
- `delete_project(project_name)`
- `get_current_project()` - Remove this tool
- `switch_project(project_name)` - Remove this tool
- `set_default_project(project_name, activate)` - Remove activate parameter
## How (High Level)
### Phase 1: Basic Memory Core (basic-memory repository)
#### MCP Tool Updates
Phase 1: Core Changes
1. Update project_context.py
- [x] Make project parameter mandatory for get_active_project()
- [x] Remove session state handling
2. Update Content Management Tools (6 tools)
- [x] write_note: Make project parameter required, not optional
- [x] read_note: Make project parameter required
- [x] edit_note: Add required project parameter
- [x] delete_note: Add required project parameter
- [x] view_note: Add required project parameter
- [x] read_content: Add required project parameter
3. Update Knowledge Graph Navigation Tools (3 tools)
- [x] build_context: Add required project parameter
- [x] recent_activity: Make project parameter required
- [x] list_directory: Add required project parameter
4. Update Search & Visualization Tools (2 tools)
- [x] search_notes: Add required project parameter
- [x] canvas: Add required project parameter
5. Update Project Management Tools
- [x] Remove switch_project tool completely
- [x] Remove get_current_project tool completely
- [x] Update set_default_project to remove activate parameter
- [x] Keep list_memory_projects, create_memory_project, delete_project unchanged
6. Enhance recent_activity Response
- [x] Add project distribution info showing activity across all projects
- [x] Include project usage stats in response
- [x] Implement ProjectActivitySummary for discovery mode
- [x] Add dual-mode functionality (discovery vs project-specific)
7. Update Tool Documentation
- [x] Update write_note docstring with stateless architecture examples
- [x] Update read_note docstring with project parameter examples
- [x] Update delete_note docstring with comprehensive usage guidance
- [x] Update all remaining tool docstrings with project parameter examples
8. Update Tool Responses
- [x] Add clear project indicator to all tool responses across all tools
- [x] Format: "project: {project_name}" in response metadata
- [x] Add project metadata footer for LLM awareness
- [x] Update all tool responses to include project indicators
9. Comprehensive Testing
- [x] Update all write_note tests to use stateless architecture (34 tests passing)
- [x] Update all edit_note tests to use stateless architecture (17 tests passing)
- [x] Update all view_note tests to use stateless architecture (12 tests passing)
- [x] Update all search_notes tests to use stateless architecture (16 tests passing)
- [x] Update all move_note tests to use stateless architecture (31 tests passing)
- [x] Update all delete_note tests to use stateless architecture
- [x] Verify direct function call compatibility (bypassing MCP layer)
- [x] Test security validation with project parameters
- [x] Validate error handling for non-existent projects
- [x] **Total: 157 tests updated and passing (100% success rate)**
- [x] **147 existing tests** updated for stateless architecture
- [x] **10 new tests** for single project constraint mode
### Phase 1.5: Default Project Mode Enhancement
#### Problem
While the stateless architecture solves reliability issues, it introduces UX friction for single-project users (estimated 80% of usage) who must specify the project parameter in every tool call.
#### Solution: Default Project Mode
Add optional `default_project_mode` configuration that allows single-project users to have the simplicity of implicit project selection while maintaining the reliability of stateless architecture.
#### Configuration
```json
{
"default_project": "main",
"default_project_mode": true // NEW: Auto-use default_project when not specified
}
```
#### Implementation Details
1. **Config Enhancement** (`src/basic_memory/config.py`)
- Add `default_project_mode: bool = Field(default=False)`
- Preserves backward compatibility (defaults to false)
2. **Project Resolution Logic** (`src/basic_memory/mcp/project_context.py`)
Three-tier resolution hierarchy:
- Priority 1: CLI `--project` constraint (BASIC_MEMORY_MCP_PROJECT env var)
- Priority 2: Explicit project parameter in tool call
- Priority 3: `default_project` if `default_project_mode=true` and no project specified
3. **Assistant Guide Updates** (`src/basic_memory/mcp/resources/ai_assistant_guide.md`)
- Detect `default_project_mode` at runtime
- Provide mode-specific instructions to LLMs
- In default mode: "All operations use project 'main' automatically"
- In regular mode: Current project discovery guidance
4. **Tool Parameter Handling** (all MCP tools)
- Make project parameter Optional[str] = None
- Add resolution logic: `project = project or get_default_project()`
- Maintain explicit project override capability
#### Usage Modes Summary
- **Regular Mode**: Multi-project users, assistant tracks project per conversation
- **Default Project Mode**: Single-project users, automatic default project
- **Constrained Mode**: CLI --project flag, locked to specific project
#### Testing Requirements
- Integration test for default_project_mode=true with missing parameters
- Test explicit project override in default_project_mode
- Test mode=false requires explicit parameters
- Test CLI constraint overrides default_project_mode
Phase 2: Testing & Validation
8. Update Tests
- [x] Modify all MCP tool tests to pass required project parameter
- [x] Remove tests for deleted tools (switch_project, get_current_project)
- [x] Add tests for project parameter validation
- [x] **Complete: All 147 tests across 5 test files updated and passing**
#### Enhanced recent_activity Response
```json
{
"recent_notes": [...],
"project_activity": {
"research-project": {
"operations": 5,
"last_used": "30 minutes ago",
"recent_folders": ["experiments", "findings"]
},
"work-notes": {
"operations": 2,
"last_used": "2 hours ago",
"recent_folders": ["meetings", "planning"]
}
},
"total_projects": 3
}
```
#### Response Format Updates
```
✓ Note created successfully
Project: research-project
File: experiments/Neural Network Results.md
Permalink: research-project/neural-network-results
```
### Phase 2: Cloud Service Simplification (basic-memory-cloud repository)
#### Remove Session Infrastructure
1. Delete `apps/mcp/src/basic_memory_cloud_mcp/middleware/session_state.py`
2. Delete `apps/mcp/src/basic_memory_cloud_mcp/middleware/session_logging.py`
3. Update `apps/mcp/src/basic_memory_cloud_mcp/main.py`:
```python
# Remove session middleware
# server.add_middleware(SessionStateMiddleware)
# Enable stateless HTTP
mcp = FastMCP(name="basic-memory-mcp", stateless_http=True)
```
#### Deployment Simplification
1. Remove Redis from `fly.toml`
2. Remove Redis environment variables
3. Update health checks to not depend on Redis
### Phase 3: Conversational Project Management
#### Claude Behavior Pattern
1. **Project Discovery**:
```
Claude: Let me check your recent activity...
[calls recent_activity() - no project needed for discovery]
I see you've been working in:
- research-project (5 operations, 30 min ago)
- work-notes (2 operations, 2 hours ago)
Which project should I use for this operation?
```
2. **Context Maintenance**:
```
User: Use research-project
Claude: Working in research-project.
[All subsequent operations use project="research-project"]
```
3. **Explicit Project Switching**:
```
User: Check work-notes for that meeting summary
Claude: Let me search work-notes for the meeting summary.
[Uses project="work-notes" for specific operation]
```
## How to Evaluate
### Success Criteria
#### 1. Functional Completeness
- [x] All MCP tools accept required `project` parameter
- [x] All MCP tools validate project exists before execution
- [x] `switch_project` and `get_current_project` tools removed
- [x] All responses display target project clearly
- [ ] No Redis dependencies in deployment (Phase 2: Cloud Service)
- [x] `recent_activity` shows project distribution with ProjectActivitySummary
#### 2. Cross-Client Compatibility Testing
Test identical operations across all clients:
- [ ] **Claude Desktop**: All operations work with explicit projects
- [ ] **Claude Code**: All operations work with explicit projects
- [ ] **Claude Mobile iOS**: All operations work with explicit projects
- [ ] **API clients**: All operations work with explicit projects
- [ ] **CLI tools**: All operations work with explicit projects
#### 3. Session Independence Verification
- [ ] Operations work identically with/without session tracking
- [ ] No behavioral differences between clients
- [ ] Mobile client session ID changes do not affect operations
- [ ] Redis can be completely removed without functional impact
#### 4. Performance & Scaling
- [ ] `stateless_http=True` enabled successfully
- [ ] No Redis memory usage
- [ ] Horizontal scaling possible (multiple MCP instances)
- [ ] Response times unchanged or improved
#### 5. User Experience Testing
**Project Discovery Flow**:
- [x] `recent_activity()` provides useful project context
- [x] Claude can intelligently suggest projects based on activity
- [x] Project switching is explicit and clear in conversation
**Error Handling**:
- [x] Clear error messages for non-existent projects
- [x] Helpful suggestions when project parameter missing
- [x] No silent failures or wrong-project operations
**Response Clarity**:
- [x] Every operation clearly shows target project
- [x] Users always know which project is being operated on
- [x] No confusion about "current project" state
#### 6. Migration Safety
- [ ] Backward compatibility period with optional project parameter
- [ ] Clear migration documentation for existing users
- [ ] Data integrity maintained during transition
- [ ] No data loss during migration
### Test Scenarios
#### Core Functionality Test
```bash
# Test all tools work with explicit project
write_note(project="test-proj", title="Test", content="Content", folder="docs")
read_note(project="test-proj", identifier="Test")
edit_note(project="test-proj", identifier="Test", operation="append", content="More")
search_notes(project="test-proj", query="Content")
list_directory(project="test-proj", dir_name="docs")
delete_note(project="test-proj", identifier="Test")
```
#### Cross-Client Consistency Test
Run identical test sequence on:
1. Claude Desktop
2. Claude Code
3. Claude Mobile iOS
4. API client
5. CLI tools
Verify all clients:
- Accept explicit project parameters
- Return identical responses
- Show same project information
- Have no session dependencies
#### Session Independence Test
1. Monitor session IDs during operations
2. Verify operations work with changing session IDs
3. Confirm Redis removal doesn't affect functionality
4. Test with multiple concurrent clients
### Acceptance Criteria
**Must Have**:
- All MCP tools require and use explicit project parameter
- No session state dependencies remain
- Universal client compatibility achieved
- Clear project information in all responses
**Should Have**:
- Enhanced `recent_activity` with project distribution
- Smooth migration path for existing users
- Improved performance with stateless architecture
**Could Have**:
- Smart project suggestions based on content/context
- Project shortcuts for common operations
- Advanced project analytics in responses
## Notes
### Breaking Changes
This is a **breaking change** that requires:
- All MCP clients to pass project parameter
- Migration of existing workflows
- Update of all documentation and examples
### Implementation Order
1. **basic-memory core** - Update MCP tools to accept project parameter (optional initially)
2. **Testing** - Verify all clients work with explicit projects
3. **Cloud service** - Remove session infrastructure
4. **Migration** - Make project parameter mandatory
5. **Cleanup** - Remove deprecated tools and middleware
### Related Issues
- Fixes #74 (Claude iOS session state bug)
- Implements #75 (Mandatory project parameter architecture)
- Enables future horizontal scaling
- Simplifies multi-tenant architecture
### Dependencies
- Requires coordination between basic-memory and basic-memory-cloud repositories
- Needs client-side updates for smooth transition
- Documentation updates across all materials
@@ -0,0 +1,193 @@
---
title: 'SPEC-7: POC to spike Tigris/Turso for local access to cloud data'
type: spec
permalink: specs/spec-7-poc-tigris-turso-local-access-cloud-data
tags:
- poc
- tigris
- turso
- cloud-storage
- architecture
- proof-of-concept
---
# SPEC-7: POC to spike Tigris/Turso for local access to cloud data
## Why
Current basic-memory-cloud architecture uses Fly volumes for tenant file storage, which creates several limitations:
1. **Storage Scalability**: Fly volumes require pre-provisioning and don't auto-scale with usage
2. **Cost Model**: Volume pricing vs object storage pricing may be less favorable at scale
3. **Local Development**: No way for users to mount their cloud tenant files locally for real-time editing
4. **Multi-Region**: Volumes are region-locked, limiting global deployment flexibility
5. **Backup/Disaster Recovery**: Object storage provides better durability and replication options
The core insight is that Basic Memory requires POSIX filesystem semantics but could benefit from object storage durability and accessibility. By combining:
- **Tigris object storage** for file persistence (via rclone mount)
- **Turso/libSQL** for SQLite indexing (replacing local .db files)
We could enable a revolutionary user experience: **local editing of cloud-stored files** while maintaining Basic Memory's existing filesystem assumptions.
## What
This specification defines a proof-of-concept to validate the technical feasibility of the Tigris/Turso architecture for basic-memory-cloud tenants.
**Affected Areas:**
- **Storage Architecture**: Replace Fly volumes with Tigris object storage
- **Database Architecture**: Replace local SQLite with Turso remote database
- **Container Setup**: Add rclone mounting in tenant containers
- **Local Development**: Enable local mounting of cloud tenant data
- **Basic Memory Core**: Validate unchanged operation over mounted filesystems
**Key Components:**
- **Tigris Storage**: S3-compatible object storage via Fly.io integration
- **rclone NFS Mount**: Native NFS mounting without FUSE dependencies
- **Turso Database**: Hosted libSQL for SQLite replacement
- **Single-Tenant Model**: One bucket + one database per tenant (simplified isolation)
## How (High Level)
### Phase 1: Local POC Validation
- [ ] Set up Tigris bucket with test data
- [ ] Configure rclone NFS mount locally
- [ ] Test Basic Memory operations over mounted filesystem
- [ ] Measure performance characteristics and identify issues
- [ ] Validate file watching, sync operations, and concurrent access patterns
### Phase 2: Database Migration
- [ ] Set up Turso account and test database
- [ ] Modify Basic Memory to accept external DATABASE_URL
- [ ] Test all operations with remote SQLite via Turso
- [ ] Validate performance and functionality parity
### Phase 3: Container Integration
- [ ] Create container image with rclone + NFS support
- [ ] Implement tenant-specific credential management
- [ ] Test container startup with automatic mounting
- [ ] Validate isolation between tenant containers
### Phase 4: Local Access Validation
- [ ] Test local rclone mounting of tenant data
- [ ] Validate real-time file editing experience
- [ ] Test conflict resolution and sync behavior
- [ ] Measure latency impact on user experience
### Architecture Overview
```
Local Development:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Local rclone │───▶│ Tigris Bucket │◀───│ Tenant Container│
│ NFS Mount │ │ (S3 storage) │ │ rclone mount │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Basic Memory │ │ Basic Memory │
│ (local files) │ │ (mounted files) │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Turso Database │◀───────────────────────────│ Turso Database │
│ (shared index) │ │ (shared index) │
└─────────────────┘ └─────────────────┘
```
## How to Evaluate
### Success Criteria
- [ ] **Filesystem Compatibility**: Basic Memory operates without modification over rclone-mounted Tigris storage
- [ ] **Performance Acceptable**: File operations complete within 2x local filesystem latency
- [ ] **Database Functionality**: All Basic Memory features work with Turso remote SQLite
- [ ] **Container Reliability**: Tenant containers start successfully with automatic mounting
- [ ] **Local Access**: Users can mount and edit cloud files locally with real-time sync
- [ ] **Data Isolation**: Tenant data remains properly isolated using bucket/database separation
### Testing Procedure
1. **Local Filesystem Test**:
```bash
# Mount Tigris bucket locally
rclone nfsmount tigris:test-bucket ~/tigris-test --vfs-cache-mode writes
# Run Basic Memory operations
cd ~/tigris-test && basic-memory sync --watch
# Test: create notes, search, file watching, bulk operations
```
2. **Database Migration Test**:
```bash
# Configure Turso connection
export DATABASE_URL="libsql://test-db.turso.io?authToken=..."
# Test all MCP tools with remote database
basic-memory tools # Test each tool functionality
```
3. **Container Integration Test**:
```dockerfile
# Test container with rclone mounting
FROM python:3.12
RUN apt-get update && apt-get install -y rclone nfs-common
# ... test startup and mounting process
```
4. **Performance Benchmarking**:
- File creation/read/write operations (target: <2x local latency)
- Search query performance (target: comparable to local SQLite)
- File watching responsiveness (target: events within 1-2 seconds)
- Concurrent operation handling
### Risk Assessment
**High Risk Items**:
- [ ] NFS-over-S3 performance may be insufficient for real-time operations
- [ ] File watching (`inotify`) over NFS may be unreliable
- [ ] Network interruptions could cause filesystem errors
- [ ] Concurrent access patterns might hit S3 rate limits
**Mitigation Strategies**:
- Comprehensive performance testing before committing to architecture
- Fallback plan to S3-native storage backend if filesystem approach fails
- Extensive error handling and retry logic for network issues
### Metrics to Track
- **Latency**: File operation response times (read/write/watch)
- **Reliability**: Success rate of file operations over time
- **Throughput**: Concurrent file operations and search queries
- **User Experience**: Perceived performance for local mounting use case
## Notes
### Key Architectural Decisions
- **Single tenant per bucket/database**: Simplifies isolation and credential management
- **Maintain POSIX compatibility**: Preserve Basic Memory's existing filesystem assumptions
- **NFS over FUSE**: Better compatibility and performance characteristics
- **Turso for SQLite**: Leverages specialized remote SQLite expertise
### Alternative Approaches Considered
- **S3-native storage backend**: Would require Basic Memory architecture changes
- **Hybrid approach**: Local files + cloud sync (adds complexity)
- **FUSE mounting**: More platform dependencies and kernel requirements
### Integration Points
- [ ] Fly.io Tigris integration for bucket provisioning
- [ ] Turso account setup and database provisioning
- [ ] Container image modifications for rclone support
- [ ] Credential management for tenant isolation
## Observations
- [architecture] Tigris/Turso split cleanly separates file storage from indexing concerns #storage-separation
- [user-experience] Local mounting of cloud files could be revolutionary for knowledge management #local-cloud-hybrid
- [compatibility] Maintaining POSIX filesystem assumptions preserves Basic Memory's local/cloud compatibility #architecture-preservation
- [simplification] Single tenant per bucket eliminates complex multi-tenancy in storage layer #tenant-isolation
- [risk] NFS-over-S3 performance characteristics are unproven for real-time operations #performance-risk
- [benefit] Object storage pricing model could be more favorable than volume pricing #cost-optimization
- [innovation] Real-time local editing of cloud-stored files addresses major SaaS limitation #competitive-advantage
## Relations
- implements [[SPEC-6 Explicit Project Parameter Architecture]]
- requires [[Fly.io Tigris Integration]]
- enables [[Local Cloud File Access]]
- alternative_to [[Fly Volume Storage]]
+886
View File
@@ -0,0 +1,886 @@
---
title: 'SPEC-8: TigrisFS Integration for Tenant API'
Date: September 22, 2025
Status: Phase 3.6 Complete - Tenant Mount API Endpoints Ready for CLI Implementation
Priority: High
Goal: Replace Fly volumes with Tigris bucket provisioning in production tenant API
permalink: spec-8-tigris-fs-integration
---
## Executive Summary
Based on SPEC-7 Phase 4 POC testing, this spec outlines productizing the TigrisFS/rclone implementation in the Basic Memory Cloud tenant API.
We're moving from proof-of-concept to production integration, replacing Fly volume storage with Tigris bucket-per-tenant architecture.
## Current Architecture (Fly Volumes)
### Tenant Provisioning Flow
```python
# apps/cloud/src/basic_memory_cloud/workflows/tenant_provisioning.py
async def provision_tenant_infrastructure(tenant_id: str):
# 1. Create Fly app
# 2. Create Fly volume ← REPLACE THIS
# 3. Deploy API container with volume mount
# 4. Configure health checks
```
### Storage Implementation
- Each tenant gets dedicated Fly volume (1GB-10GB)
- Volume mounted at `/app/data` in API container
- Local filesystem storage with Basic Memory indexing
- No global caching or edge distribution
## Proposed Architecture (Tigris Buckets)
### New Tenant Provisioning Flow
```python
async def provision_tenant_infrastructure(tenant_id: str):
# 1. Create Fly app
# 2. Create Tigris bucket with admin credentials ← NEW
# 3. Store bucket name in tenant record ← NEW
# 4. Deploy API container with TigrisFS mount using admin credentials
# 5. Configure health checks
```
### Storage Implementation
- Each tenant gets dedicated Tigris bucket
- TigrisFS mounts bucket at `/app/data` in API container
- Global edge caching and distribution
- Configurable cache TTL for sync performance
## Implementation Plan
### Phase 1: Bucket Provisioning Service
**✅ IMPLEMENTED: StorageClient with Admin Credentials**
```python
# apps/cloud/src/basic_memory_cloud/clients/storage_client.py
class StorageClient:
async def create_tenant_bucket(self, tenant_id: UUID) -> TigrisBucketCredentials
async def delete_tenant_bucket(self, tenant_id: UUID, bucket_name: str) -> bool
async def list_buckets(self) -> list[TigrisBucketResponse]
async def test_tenant_credentials(self, credentials: TigrisBucketCredentials) -> bool
```
**Simplified Architecture Using Admin Credentials:**
- Single admin access key with full Tigris permissions (configured in console)
- No tenant-specific IAM user creation needed
- Bucket-per-tenant isolation for logical separation
- Admin credentials shared across all tenant operations
**Integrate with Provisioning workflow:**
```python
# Update tenant_provisioning.py
async def provision_tenant_infrastructure(tenant_id: str):
storage_client = StorageClient(settings.aws_access_key_id, settings.aws_secret_access_key)
bucket_creds = await storage_client.create_tenant_bucket(tenant_id)
await store_bucket_name(tenant_id, bucket_creds.bucket_name)
await deploy_api_with_tigris(tenant_id, bucket_creds)
```
### Phase 2: Simplified Bucket Management
**✅ SIMPLIFIED: Admin Credentials + Bucket Names Only**
Since we use admin credentials for all operations, we only need to track bucket names per tenant:
1. **Primary Storage (Fly Secrets)**
```bash
flyctl secrets set -a basic-memory-{tenant_id} \
AWS_ACCESS_KEY_ID="{admin_access_key}" \
AWS_SECRET_ACCESS_KEY="{admin_secret_key}" \
AWS_ENDPOINT_URL_S3="https://fly.storage.tigris.dev" \
AWS_REGION="auto" \
BUCKET_NAME="basic-memory-{tenant_id}"
```
2. **Database Storage (Bucket Name Only)**
```python
# apps/cloud/src/basic_memory_cloud/models/tenant.py
class Tenant(BaseModel):
# ... existing fields
tigris_bucket_name: Optional[str] = None # Just store bucket name
tigris_region: str = "auto"
created_at: datetime
```
**Benefits of Simplified Approach:**
- No credential encryption/decryption needed
- Admin credentials managed centrally in environment
- Only bucket names stored in database (not sensitive)
- Simplified backup/restore scenarios
- Reduced security attack surface
### Phase 3: API Container Updates
**Update API container configuration:**
```dockerfile
# apps/api/Dockerfile
# Add TigrisFS installation
RUN curl -L https://github.com/tigrisdata/tigrisfs/releases/latest/download/tigrisfs-linux-amd64 \
-o /usr/local/bin/tigrisfs && chmod +x /usr/local/bin/tigrisfs
```
**Startup script integration:**
```bash
# apps/api/tigrisfs-startup.sh (already exists)
# Mount TigrisFS → Start Basic Memory API
exec python -m basic_memory_cloud_api.main
```
**Fly.toml environment (optimized for < 5s startup):**
```toml
# apps/api/fly.tigris-production.toml
[env]
TIGRISFS_MEMORY_LIMIT = '1024' # Reduced for faster init
TIGRISFS_MAX_FLUSHERS = '16' # Fewer threads for faster startup
TIGRISFS_STAT_CACHE_TTL = '30s' # Balance sync speed vs startup
TIGRISFS_LAZY_INIT = 'true' # Enable lazy loading
BASIC_MEMORY_HOME = '/app/data'
# Suspend optimization for wake-on-network
[machine]
auto_stop_machines = "suspend" # Faster than full stop
auto_start_machines = true
min_machines_running = 0
```
### Phase 4: Local Access Features
**CLI automation for local mounting:**
```python
# New CLI command: basic-memory cloud mount
async def setup_local_mount(tenant_id: str):
# 1. Fetch bucket credentials from cloud API
# 2. Configure rclone with scoped IAM policy
# 3. Mount via rclone nfsmount (macOS) or FUSE (Linux)
# 4. Start Basic Memory sync watcher
```
**Local mount configuration:**
```bash
# rclone config for tenant
rclone mount basic-memory-{tenant_id}: ~/basic-memory-{tenant_id} \
--nfs-mount \
--vfs-cache-mode writes \
--cache-dir ~/.cache/rclone/basic-memory-{tenant_id}
```
### Phase 5: TigrisFS Cache Sync Solutions
**Problem**: When files are uploaded via CLI/bisync, the tenant API container doesn't see them immediately due to TigrisFS cache (30s TTL) and lack of inotify events on mounted filesystems.
**Multi-Layer Solution:**
**Layer 1: API Sync Endpoint** (Immediate)
```python
# POST /sync - Force TigrisFS cache refresh
# Callable by CLI after uploads
subprocess.run(["sync", "fsync /app/data"], check=True)
```
**Layer 2: Tigris Webhook Integration** (Real-time)
https://www.tigrisdata.com/docs/buckets/object-notifications/#webhook
```python
# Webhook endpoint for bucket changes
@app.post("/webhooks/tigris/{tenant_id}")
async def handle_bucket_notification(tenant_id: str, event: TigrisEvent):
if event.eventName in ["OBJECT_CREATED_PUT", "OBJECT_DELETED"]:
await notify_container_sync(tenant_id, event.object.key)
```
**Layer 3: CLI Sync Notification** (User-triggered)
```bash
# CLI calls container sync endpoint after successful bisync
basic-memory cloud bisync # Automatically notifies container
curl -X POST https://basic-memory-{tenant-id}.fly.dev/sync
```
**Layer 4: Periodic Sync Fallback** (Safety net)
```python
# Background task: fsync /app/data every 30s as fallback
# Ensures eventual consistency even if other layers fail
```
**Implementation Priority:**
1. Layer 1 (API endpoint) - Quick testing capability
2. Layer 3 (CLI integration) - Improved UX
3. Layer 4 (Periodic fallback) - Safety net
4. Layer 2 (Webhooks) - Production real-time sync
## Performance Targets
### Sync Latency
- **Target**: < 5 seconds local→cloud→container
- **Configuration**: `TIGRISFS_STAT_CACHE_TTL = '5s'`
- **Monitoring**: Track sync metrics in production
### Container Startup
- **Target**: < 5 seconds including TigrisFS mount
- **Fast retry**: 0.5s intervals for mount verification
- **Fallback**: Container fails fast if mount fails
### Memory Usage
- **TigrisFS cache**: 2GB memory limit per container
- **Concurrent uploads**: 32 flushers max
- **VM sizing**: shared-cpu-2x (2048mb) minimum
## Security Considerations
### Bucket Isolation
- Each tenant has dedicated bucket
- IAM policies prevent cross-tenant access
- No shared bucket with subdirectories
### Credential Security
- Fly secrets for runtime access
- Encrypted database backup for disaster recovery
- Credential rotation capability
### Data Residency
- Tigris global edge caching
- SOC2 Type II compliance
- Encryption at rest and in transit
## Operational Benefits
### Scalability
- Horizontal scaling with stateless API containers
- Global edge distribution
- Better resource utilization
### Reliability
- No cold starts between tenants
- Built-in redundancy and caching
- Simplified backup strategy
### Cost Efficiency
- Pay-per-use storage pricing
- Shared infrastructure benefits
- Reduced operational overhead
## Risk Mitigation
### Data Loss Prevention
- Dual credential storage (Fly + database)
- Automated backup workflows to R2/S3
- Tigris built-in redundancy
### Performance Degradation
- Configurable cache settings per tenant
- Monitoring and alerting on sync latency
- Fallback to volume storage if needed
### Security Vulnerabilities
- Bucket-per-tenant isolation
- Regular credential rotation
- Security scanning and monitoring
## Success Metrics
### Technical Metrics
- Sync latency P50 < 5 seconds
- Container startup time < 5 seconds
- Zero data loss incidents
- 99.9% uptime per tenant
### Business Metrics
- Reduced infrastructure costs vs volumes
- Improved user experience with faster sync
- Enhanced enterprise security posture
- Simplified operational overhead
## Open Questions
1. **Tigris rate limits**: What are the API limits for bucket creation?
2. **Cost analysis**: What's the break-even point vs Fly volumes?
3. **Regional preferences**: Should enterprise customers choose regions?
4. **Backup retention**: How long to keep automated backups?
## Implementation Checklist
### Phase 1: Bucket Provisioning Service ✅ COMPLETED
- [x] **Research Tigris bucket API** - Document bucket creation and S3 API compatibility
- [x] **Create StorageClient class** - Implemented with admin credentials and comprehensive integration tests
- [x] **Test bucket creation** - Full test suite validates API integration with real Tigris environment
- [x] **Add bucket provisioning to DBOS workflow** - Integrated StorageClient with tenant_provisioning.py
### Phase 2: Simplified Bucket Management ✅ COMPLETED
- [x] **Update Tenant model** with tigris_bucket_name field (replaced fly_volume_id)
- [x] **Implement bucket name storage** - Database migration and model updates completed
- [x] **Test bucket provisioning integration** - Full test suite validates workflow from tenant creation to bucket assignment
- [x] **Remove volume logic from all tests** - Complete migration from volume-based to bucket-based architecture
### Phase 3: API Container Integration ✅ COMPLETED
- [x] **Update Dockerfile** to install TigrisFS binary in API container with configurable version
- [x] **Optimize tigrisfs-startup.sh** with production-ready security and reliability improvements
- [x] **Create production-ready container** with proper signal handling and mount validation
- [x] **Implement security fixes** based on Claude code review (conditional debug, credential protection)
- [x] **Add proper process supervision** with cleanup traps and error handling
- [x] **Remove debug artifacts** - Cleaned up all debug Dockerfiles and test scripts
### Phase 3.5: IAM Access Key Management ✅ COMPLETED
- [x] **Research Tigris IAM API** - Documented create_policy, attach_user_policy, delete_access_key operations
- [x] **Implement bucket-scoped credential generation** - StorageClient.create_tenant_access_keys() with IAM policies
- [x] **Add comprehensive security test suite** - 5 security-focused integration tests covering all attack vectors
- [x] **Verify cross-bucket access prevention** - Scoped credentials can ONLY access their designated bucket
- [x] **Test credential lifecycle management** - Create, validate, delete, and revoke access keys
- [x] **Validate admin vs scoped credential isolation** - Different access patterns and security boundaries
- [x] **Test multi-tenant isolation** - Multiple tenants cannot access each other's buckets
### Phase 3.6: Tenant Mount API Endpoints ✅ COMPLETED
- [x] **Implement GET /tenant/mount/info** - Returns mount info without exposing credentials
- [x] **Implement POST /tenant/mount/credentials** - Creates new bucket-scoped credentials for CLI mounting
- [x] **Implement DELETE /tenant/mount/credentials/{cred_id}** - Revoke specific credentials with proper cleanup
- [x] **Implement GET /tenant/mount/credentials** - List active credentials without exposing secrets
- [x] **Add TenantMountCredentials database model** - Tracks credential metadata (no secret storage)
- [x] **Create comprehensive test suite** - 28 tests covering all scenarios including multi-session support
- [x] **Implement multi-session credential flow** - Multiple active credentials per tenant supported
- [x] **Secure credential handling** - Secret keys never stored, returned once only for immediate use
- [x] **Add dependency injection for StorageClient** - Clean integration with existing API architecture
- [x] **Fix Tigris configuration for cloud service** - Added AWS environment variables to fly.template.toml
- [x] **Update tenant machine configurations** - Include AWS credentials for TigrisFS mounting with clear credential strategy
**Security Test Results:**
```
✅ Cross-bucket access prevention - PASS
✅ Deleted credentials access revoked - PASS
✅ Invalid credentials rejected - PASS
✅ Admin vs scoped credential isolation - PASS
✅ Multiple scoped credentials isolation - PASS
```
**Implementation Details:**
- Uses Tigris IAM managed policies (create_policy + attach_user_policy)
- Bucket-scoped S3 policies with Actions: GetObject, PutObject, DeleteObject, ListBucket
- Resource ARNs limited to specific bucket: `arn:aws:s3:::bucket-name` and `arn:aws:s3:::bucket-name/*`
- Access keys follow Tigris format: `tid_` prefix with secure random suffix
- Complete cleanup on deletion removes both access keys and associated policies
### Phase 4: Local Access CLI
- [x] **Design local mount CLI command** for automated rclone configuration
- [x] **Implement credential fetching** from cloud API for local setup
- [x] **Create rclone config automation** for tenant-specific bucket mounting
- [x] **Test local→cloud→container sync** with optimized cache settings
- [x] **Document local access setup** for beta users
### Phase 5: Webhook Integration (Future)
- [ ] **Research Tigris webhook API** for object notifications and payload format
- [ ] **Design webhook endpoint** for real-time sync notifications
- [ ] **Implement notification handling** to trigger Basic Memory sync events
- [ ] **Test webhook delivery** and sync latency improvements
## Success Metrics
- [ ] **Container startup < 5 seconds** including TigrisFS mount and Basic Memory init
- [ ] **Sync latency < 5 seconds** for local→cloud→container file changes
- [ ] **Zero data loss** during bucket provisioning and credential management
- [ ] **100% test coverage** for new TigrisBucketService and credential functions
- [ ] **Beta deployment** with internal users validating local-cloud workflow
## Implementation Notes
## Phase 4.1: Bidirectional Sync with rclone bisync (NEW)
### Problem Statement
During testing, we discovered that some applications (particularly Obsidian) don't detect file changes over NFS mounts. Rather than building a custom sync daemon, we can leverage `rclone bisync` - rclone's built-in bidirectional synchronization feature.
### Solution: rclone bisync
Use rclone's proven bidirectional sync instead of custom implementation:
**Core Architecture:**
```bash
# rclone bisync handles all the complexity
rclone bisync ~/basic-memory-{tenant_id} basic-memory-{tenant_id}:{bucket_name} \
--create-empty-src-dirs \
--conflict-resolve newer \
--resilient \
--check-access
```
**Key Benefits:**
- ✅ **Battle-tested**: Production-proven rclone functionality
- ✅ **MIT licensed**: Open source with permissive licensing
- ✅ **No custom code**: Zero maintenance burden for sync logic
- ✅ **Built-in safety**: max-delete protection, conflict resolution
- ✅ **Simple installation**: Works with Homebrew rclone (no FUSE needed)
- ✅ **File watcher compatible**: Works with Obsidian and all applications
- ✅ **Offline support**: Can work offline and sync when connected
### bisync Conflict Resolution Options
**Built-in conflict strategies:**
```bash
--conflict-resolve none # Keep both files with .conflict suffixes (safest)
--conflict-resolve newer # Always pick the most recently modified file
--conflict-resolve larger # Choose based on file size
--conflict-resolve path1 # Always prefer local changes
--conflict-resolve path2 # Always prefer cloud changes
```
### Sync Profiles Using bisync
**Profile configurations:**
```python
BISYNC_PROFILES = {
"safe": {
"conflict_resolve": "none", # Keep both versions
"max_delete": 10, # Prevent mass deletion
"check_access": True, # Verify sync integrity
"description": "Safe mode with conflict preservation"
},
"balanced": {
"conflict_resolve": "newer", # Auto-resolve to newer file
"max_delete": 25,
"check_access": True,
"description": "Balanced mode (recommended default)"
},
"fast": {
"conflict_resolve": "newer",
"max_delete": 50,
"check_access": False, # Skip verification for speed
"description": "Fast mode for rapid iteration"
}
}
```
### CLI Commands
**Manual sync commands:**
```bash
basic-memory cloud bisync # Manual bidirectional sync
basic-memory cloud bisync --dry-run # Preview changes
basic-memory cloud bisync --profile safe # Use specific profile
basic-memory cloud bisync --resync # Force full baseline resync
```
**Watch mode (Step 1):**
```bash
basic-memory cloud bisync --watch # Long-running process, sync every 60s
basic-memory cloud bisync --watch --interval 30s # Custom interval
```
**System integration (Step 2 - Future):**
```bash
basic-memory cloud bisync-service install # Install as system service
basic-memory cloud bisync-service start # Start background service
basic-memory cloud bisync-service status # Check service status
```
### Implementation Strategy
**Phase 4.1.1: Core bisync Implementation**
- [ ] Implement `run_bisync()` function wrapping rclone bisync
- [ ] Add profile-based configuration (safe/balanced/fast)
- [ ] Create conflict resolution and safety options
- [ ] Test with sample files and conflict scenarios
**Phase 4.1.2: Watch Mode**
- [ ] Add `--watch` flag for continuous sync
- [ ] Implement configurable sync intervals
- [ ] Add graceful shutdown and signal handling
- [ ] Create status monitoring and progress indicators
**Phase 4.1.3: User Experience**
- [ ] Add conflict reporting and resolution guidance
- [ ] Implement dry-run preview functionality
- [ ] Create troubleshooting and diagnostic commands
- [ ] Add filtering configuration (.gitignore-style)
**Phase 4.1.4: System Integration (Future)**
- [ ] Generate platform-specific service files (launchd/systemd)
- [ ] Add service management commands
- [ ] Implement automatic startup and recovery
- [ ] Create monitoring and logging integration
### Technical Implementation
**Core bisync wrapper:**
```python
def run_bisync(
tenant_id: str,
bucket_name: str,
profile: str = "balanced",
dry_run: bool = False
) -> bool:
"""Run rclone bisync with specified profile."""
local_path = Path.home() / f"basic-memory-{tenant_id}"
remote_path = f"basic-memory-{tenant_id}:{bucket_name}"
profile_config = BISYNC_PROFILES[profile]
cmd = [
"rclone", "bisync",
str(local_path), remote_path,
"--create-empty-src-dirs",
"--resilient",
f"--conflict-resolve={profile_config['conflict_resolve']}",
f"--max-delete={profile_config['max_delete']}",
"--filters-file", "~/.basic-memory/bisync-filters.txt"
]
if profile_config.get("check_access"):
cmd.append("--check-access")
if dry_run:
cmd.append("--dry-run")
return subprocess.run(cmd, check=True).returncode == 0
```
**Default filter file (~/.basic-memory/bisync-filters.txt):**
```
- .DS_Store
- .git/**
- __pycache__/**
- *.pyc
- .pytest_cache/**
- node_modules/**
- .conflict-*
- Thumbs.db
- desktop.ini
```
**Advantages Over Custom Daemon:**
- ✅ **Zero maintenance**: No custom sync logic to debug/maintain
- ✅ **Production proven**: Used by thousands in production
- ✅ **Safety features**: Built-in max-delete, conflict handling, recovery
- ✅ **Filtering**: Advanced exclude patterns and rules
- ✅ **Performance**: Optimized for various storage backends
- ✅ **Community support**: Extensive documentation and community
## Phase 4.2: NFS Mount Support (Direct Access)
### Solution: rclone nfsmount
Keep the existing NFS mount functionality for users who prefer direct file access:
**Core Architecture:**
```bash
# rclone nfsmount provides transparent file access
rclone nfsmount basic-memory-{tenant_id}:{bucket_name} ~/basic-memory-{tenant_id} \
--vfs-cache-mode writes \
--dir-cache-time 10s \
--daemon
```
**Key Benefits:**
- ✅ **Real-time access**: Files appear immediately as they're created/modified
- ✅ **Transparent**: Works with any application that reads/writes files
- ✅ **Low latency**: Direct access without sync delays
- ✅ **Simple**: No periodic sync commands needed
- ✅ **Homebrew compatible**: Works with Homebrew rclone (no FUSE required)
**Limitations:**
- ❌ **File watcher compatibility**: Some apps (Obsidian) don't detect changes over NFS
- ❌ **Network dependency**: Requires active connection to cloud storage
- ❌ **Potential conflicts**: Simultaneous edits from multiple locations can cause issues
### Mount Profiles (Existing)
**Already implemented profiles from SPEC-7 testing:**
```python
MOUNT_PROFILES = {
"fast": {
"cache_time": "5s",
"poll_interval": "3s",
"description": "Ultra-fast development (5s sync)"
},
"balanced": {
"cache_time": "10s",
"poll_interval": "5s",
"description": "Fast development (10-15s sync, recommended)"
},
"safe": {
"cache_time": "15s",
"poll_interval": "10s",
"description": "Conflict-aware mount with backup",
"extra_args": ["--conflict-suffix", ".conflict-{DateTimeExt}"]
}
}
```
### CLI Commands (Existing)
**Mount commands already implemented:**
```bash
basic-memory cloud mount # Mount with balanced profile
basic-memory cloud mount --profile fast # Ultra-fast caching
basic-memory cloud mount --profile safe # Conflict detection
basic-memory cloud unmount # Clean unmount
basic-memory cloud mount-status # Show mount status
```
## User Choice: Mount vs Bisync
### When to Use Each Approach
| Use Case | Recommended Solution | Why |
|----------|---------------------|-----|
| **Obsidian users** | `bisync` | File watcher support for live preview |
| **CLI/vim/emacs users** | `mount` | Direct file access, lower latency |
| **Offline work** | `bisync` | Can work offline, sync when connected |
| **Real-time collaboration** | `mount` | Immediate visibility of changes |
| **Multiple machines** | `bisync` | Better conflict handling |
| **Single machine** | `mount` | Simpler, more transparent |
| **Development work** | Either | Both work well, user preference |
| **Large files** | `mount` | Streaming access vs full download |
### Installation Simplicity
**Both approaches now use simple Homebrew installation:**
```bash
# Single installation command for both approaches
brew install rclone
# No macFUSE, no system modifications needed
# Works immediately with both mount and bisync
```
### Implementation Status
**Phase 4.1: bisync** (NEW)
- [ ] Implement bisync command wrapper
- [ ] Add watch mode with configurable intervals
- [ ] Create conflict resolution workflows
- [ ] Add filtering and safety options
**Phase 4.2: mount** (EXISTING - ✅ IMPLEMENTED)
- [x] NFS mount commands with profile support
- [x] Mount management and cleanup
- [x] Process monitoring and health checks
- [x] Credential integration with cloud API
**Both approaches share:**
- [x] Credential management via cloud API
- [x] Secure rclone configuration
- [x] Tenant isolation and bucket scoping
- [x] Simple Homebrew rclone installation
Key Features:
1. Cross-Platform rclone Installation (rclone_installer.py):
- macOS: Homebrew → official script fallback
- Linux: snap → apt → official script fallback
- Windows: winget → chocolatey → scoop fallback
- Automatic version detection and verification
2. Smart rclone Configuration (rclone_config.py):
- Automatic tenant-specific config generation
- Three optimized mount profiles from your SPEC-7 testing:
- fast: 5s sync (ultra-performance)
- balanced: 10-15s sync (recommended default)
- safe: 15s sync + conflict detection
- Backup existing configs before modification
3. Robust Mount Management (mount_commands.py):
- Automatic tenant credential generation
- Mount path management (~/basic-memory-{tenant-id})
- Process lifecycle management (prevent duplicate mounts)
- Orphaned process cleanup
- Mount verification and health checking
4. Clean Architecture (api_client.py):
- Separated API client to avoid circular imports
- Reuses existing authentication infrastructure
- Consistent error handling and logging
User Experience:
One-Command Setup:
basic-memory cloud setup
```bash
# 1. Installs rclone automatically
# 2. Authenticates with existing login
# 3. Generates secure credentials
# 4. Configures rclone
# 5. Performs initial mount
```
Profile-Based Mounting:
basic-memory cloud mount --profile fast # 5s sync
basic-memory cloud mount --profile balanced # 15s sync (default)
basic-memory cloud mount --profile safe # conflict detection
Status Monitoring:
basic-memory cloud mount-status
```bash
# Shows: tenant info, mount path, sync profile, rclone processes
```
### local mount api
Endpoint 1: Get Tenant Info for user
Purpose: Get tenant details for mounting
- pass in jwt
- service returns mount info
**✅ IMPLEMENTED API Specification:**
**Endpoint 1: GET /tenant/mount/info**
- Purpose: Get tenant mount information without exposing credentials
- Authentication: JWT token (tenant_id extracted from claims)
Request:
```
GET /tenant/mount/info
Authorization: Bearer {jwt_token}
```
Response:
```json
{
"tenant_id": "434252dd-d83b-4b20-bf70-8a950ff875c4",
"bucket_name": "basic-memory-434252dd",
"has_credentials": true,
"credentials_created_at": "2025-09-22T16:48:50.414694"
}
```
**Endpoint 2: POST /tenant/mount/credentials**
- Purpose: Generate NEW bucket-scoped S3 credentials for rclone mounting
- Authentication: JWT token (tenant_id extracted from claims)
- Multi-session: Creates new credentials without revoking existing ones
Request:
```
POST /tenant/mount/credentials
Authorization: Bearer {jwt_token}
Content-Type: application/json
```
*Note: No request body needed - tenant_id extracted from JWT*
Response:
```json
{
"tenant_id": "434252dd-d83b-4b20-bf70-8a950ff875c4",
"bucket_name": "basic-memory-434252dd",
"access_key": "test_access_key_12345",
"secret_key": "test_secret_key_abcdef",
"endpoint_url": "https://fly.storage.tigris.dev",
"region": "auto"
}
```
**🔒 Security Notes:**
- Secret key returned ONCE only - never stored in database
- Credentials are bucket-scoped (cannot access other tenants' buckets)
- Multiple active credentials supported per tenant (work laptop + personal machine)
Implementation Notes
Security:
- Both endpoints require JWT authentication
- Extract tenant_id from JWT claims (not request body)
- Generate scoped credentials (not admin credentials)
- Credentials should have bucket-specific access only
Integration Points:
- Use your existing StorageClient from SPEC-8 implementation
- Leverage existing JWT middleware for tenant extraction
- Return same credential format as your Tigris bucket provisioning
Error Handling:
- 401 if not authenticated
- 403 if tenant doesn't exist
- 500 if credential generation fails
**🔄 Design Decisions:**
1. **Secure Credential Flow (No Secret Storage)**
Based on CLI flow analysis, we follow security best practices:
- ✅ API generates both access_key + secret_key via Tigris IAM
- ✅ Returns both in API response for immediate use
- ✅ CLI uses credentials immediately to configure rclone
- ✅ Database stores only metadata (access_key + policy_arn for cleanup)
- ✅ rclone handles secure local credential storage
- ❌ **Never store secret_key in database (even encrypted)**
2. **CLI Credential Flow**
```bash
# CLI calls API
POST /tenant/mount/credentials → {access_key, secret_key, ...}
# CLI immediately configures rclone
rclone config create basic-memory-{tenant_id} s3 \
access_key_id={access_key} \
secret_access_key={secret_key} \
endpoint=https://fly.storage.tigris.dev
# Database tracks metadata only
INSERT INTO tenant_mount_credentials (tenant_id, access_key, policy_arn, ...)
```
3. **Multiple Sessions Supported**
- Users can have multiple active credential sets (work laptop, personal machine, etc.)
- Each credential generation creates a new Tigris access key
- List active credentials via API (shows access_key but never secret)
4. **Failure Handling & Cleanup**
- **Happy Path**: Credentials created → Used immediately → rclone configured
- **Orphaned Credentials**: Background job revokes unused credentials
- **API Failure Recovery**: Retry Tigris deletion with stored policy_arn
- **Status Tracking**: Track tigris_deletion_status (pending/completed/failed)
5. **Event Sourcing & Audit**
- MountCredentialCreatedEvent
- MountCredentialRevokedEvent
- MountCredentialOrphanedEvent (for cleanup)
- Full audit trail for security compliance
6. **Tenant/Bucket Validation**
- Verify tenant exists and has valid bucket before credential generation
- Use existing StorageClient to validate bucket access
- Prevent credential generation for inactive/invalid tenants
📋 **Implemented API Endpoints:**
```
✅ IMPLEMENTED:
GET /tenant/mount/info # Get tenant/bucket info (no credentials exposed)
POST /tenant/mount/credentials # Generate new credentials (returns secret once)
GET /tenant/mount/credentials # List active credentials (no secrets)
DELETE /tenant/mount/credentials/{cred_id} # Revoke specific credentials
```
**API Implementation Status:**
- ✅ **GET /tenant/mount/info**: Returns tenant_id, bucket_name, has_credentials, credentials_created_at
- ✅ **POST /tenant/mount/credentials**: Creates new bucket-scoped access keys, returns access_key + secret_key once
- ✅ **GET /tenant/mount/credentials**: Lists active credentials without exposing secret keys
- ✅ **DELETE /tenant/mount/credentials/{cred_id}**: Revokes specific credentials with proper Tigris IAM cleanup
- ✅ **Multi-session support**: Multiple active credentials per tenant (work laptop + personal machine)
- ✅ **Security**: Secret keys never stored in database, returned once only for immediate use
- ✅ **Comprehensive test suite**: 28 tests covering all scenarios including error handling and multi-session flows
- ✅ **Dependency injection**: Clean integration with existing FastAPI architecture
- ✅ **Production-ready configuration**: Tigris credentials properly configured for tenant machines
🗄️ **Secure Database Schema:**
```sql
CREATE TABLE tenant_mount_credentials (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenant(id),
access_key VARCHAR(255) NOT NULL,
-- secret_key REMOVED - never store secrets (security best practice)
policy_arn VARCHAR(255) NOT NULL, -- For Tigris IAM cleanup
tigris_deletion_status VARCHAR(20) DEFAULT 'pending', -- Track cleanup
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
revoked_at TIMESTAMP NULL,
last_used_at TIMESTAMP NULL, -- Track usage for orphan cleanup
description VARCHAR(255) DEFAULT 'CLI mount credentials'
);
```
**Security Benefits:**
- ✅ Database breach cannot expose secrets
- ✅ Follows "secrets don't persist" security principle
- ✅ Meets compliance requirements (SOC2, etc.)
- ✅ Reduced attack surface
- ✅ CLI gets credentials once and stores securely via rclone
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,390 @@
---
title: 'SPEC-9-1 Follow-Ups: Conflict, Sync, and Observability'
type: tasklist
permalink: specs/spec-9-follow-ups-conflict-sync-and-observability
related: specs/spec-9-multi-project-bisync
status: revised
revision_date: 2025-10-03
---
# SPEC-9-1 Follow-Ups: Conflict, Sync, and Observability
**REVISED 2025-10-03:** Simplified to leverage rclone built-ins instead of custom conflict handling.
**Context:** SPEC-9 delivered multi-project bidirectional sync and a unified CLI. This follow-up focuses on **observability and safety** using rclone's built-in capabilities rather than reinventing conflict handling.
**Design Philosophy: "Be Dumb Like Git"**
- Let rclone bisync handle conflict detection (it already does this)
- Make conflicts visible and recoverable, don't prevent them
- Cloud is always the winner on conflict (cloud-primary model)
- Users who want version history can just use Git locally in their sync directory
**What Changed from Original Version:**
- **Replaced:** Custom `.bmmeta` sidecars → Use rclone's `.bisync/` state tracking
- **Replaced:** Custom conflict detection → Use rclone bisync 3-way merge
- **Replaced:** Tombstone files → rclone delete tracking handles this
- **Replaced:** Distributed lease → Local process lock only (document multi-device warning)
- **Replaced:** S3 versioning service → Users just use Git locally if they want history
- **Deferred:** SPEC-14 Git integration → Postponed to teams/multi-user features
## ✅ Now
- [ ] **Local process lock**: Prevent concurrent bisync runs on same device (`~/.basic-memory/sync.lock`)
- [ ] **Structured sync reports**: Parse rclone bisync output into JSON reports (creates/updates/deletes/conflicts, bytes, duration); `bm sync --report`
- [ ] **Multi-device warning**: Document that users should not run `--watch` on multiple devices simultaneously
- [ ] **Version control guidance**: Document pattern for users to use Git locally in their sync directory if they want version history
- [ ] **Docs polish**: cloud-mode toggle, mount↔bisync directory isolation, conflict semantics, quick start, migration guide, short demo clip/GIF
## 🔜 Next
- [ ] **Observability commands**: `bm conflicts list`, `bm sync history` to view sync reports and conflicts
- [ ] **Conflict resolution UI**: `bm conflicts resolve <file>` to interactively pick winner from conflict files
- [ ] **Selective sync**: allow include/exclude by project; per-project profile (safe/balanced/fast)
## 🧭 Later
- [ ] **Near real-time sync**: File watcher → targeted `rclone copy` for individual files (keep bisync as backstop)
- [ ] **Sharing / scoped tokens**: cross-tenant/project access
- [ ] **Bandwidth controls & backpressure**: policy for large repos
- [ ] **Client-side encryption (optional)**: with clear trade-offs
## 📏 Acceptance criteria (for "Now" items)
- [ ] Local process lock prevents concurrent bisync runs on same device
- [ ] rclone bisync conflict files visible and documented (`file.conflict1.md`, `file.conflict2.md`)
- [ ] `bm sync --report` generates parsable JSON with sync statistics
- [ ] Documentation clearly warns about multi-device `--watch` mode
- [ ] Documentation shows users how to use Git locally for version history
## What We're NOT Building (Deferred to rclone)
- ❌ Custom `.bmmeta` sidecars (rclone tracks state in `.bisync/` workdir)
- ❌ Custom conflict detection (rclone bisync already does 3-way merge detection)
- ❌ Tombstone files (S3 versioning + rclone delete tracking handles this)
- ❌ Distributed lease (low probability issue, rclone detects state divergence)
- ❌ Rename/move tracking (rclone has size+modtime heuristics built-in)
## Implementation Summary
**Current State (SPEC-9):**
- ✅ rclone bisync with 3 profiles (safe/balanced/fast)
-`--max-delete` safety limits (10/25/50 files)
-`--conflict-resolve=newer` for auto-resolution
- ✅ Watch mode: `bm sync --watch` (60s intervals)
- ✅ Integrity checking: `bm cloud check`
- ✅ Mount vs bisync directory isolation
**What's Needed (This Spec):**
1. **Process lock** - Simple file-based lock in `~/.basic-memory/sync.lock`
2. **Sync reports** - Parse rclone output, save to `~/.basic-memory/sync-history/`
3. **Documentation** - Multi-device warnings, conflict resolution workflow, Git usage pattern
**User Model:**
- Cloud is always the winner on conflict (cloud-primary)
- rclone creates `.conflict` files for divergent edits
- Users who want version history just use Git in their local sync directory
- Users warned: don't run `--watch` on multiple devices
## Decision Rationale & Trade-offs
### Why Trust rclone Instead of Custom Conflict Handling?
**rclone bisync already provides:**
- 3-way merge detection (compares local, remote, and last-known state)
- File state tracking in `.bisync/` workdir (hashes, modtimes)
- Automatic conflict file creation: `file.conflict1.md`, `file.conflict2.md`
- Rename detection via size+modtime heuristics
- Delete tracking (prevents resurrection of deleted files)
- Battle-tested with extensive edge case handling
**What we'd have to build with custom approach:**
- Per-file metadata tracking (`.bmmeta` sidecars)
- 3-way diff algorithm
- Conflict detection logic
- Tombstone files for deletes
- Rename/move detection
- Testing for all edge cases
**Decision:** Use what rclone already does well. Don't reinvent the wheel.
### Why Let Users Use Git Locally Instead of Building Versioning?
**The simplest solution: Just use Git**
Users who want version history can literally just use Git in their sync directory:
```bash
cd ~/basic-memory-cloud-sync/
git init
git add .
git commit -m "backup"
# Push to their own GitHub if they want
git remote add origin git@github.com:user/my-knowledge.git
git push
```
**Why this is perfect:**
- ✅ We build nothing
- ✅ Users who want Git... just use Git
- ✅ Users who don't care... don't need to
- ✅ rclone bisync already handles sync conflicts
- ✅ Users own their data, they can version it however they want (Git, Time Machine, etc.)
**What we'd have to build for S3 versioning:**
- API to enable versioning on Tigris buckets
- **Problem**: Tigris doesn't support S3 bucket versioning
- Restore commands: `bm cloud restore --version-id`
- Version listing: `bm cloud versions <path>`
- Lifecycle policies for version retention
- Documentation and user education
**What we'd have to build for SPEC-14 Git integration:**
- Committer service (daemon watching `/app/data/`)
- Puller service (webhook handler for GitHub pushes)
- Git LFS for large files
- Loop prevention between Git ↔ bisync ↔ local
- Merge conflict handling at TWO layers (rclone + Git)
- Webhook infrastructure and monitoring
**Decision:** Don't build version control. Document the pattern. "The easiest problem to solve is the one you avoid."
**When to revisit:** Teams/multi-user features where server-side version control becomes necessary for collaboration.
### Why No Distributed Lease?
**Low probability issue:**
- Requires user to manually run `bm sync` on multiple devices at exact same time
- Most users run `--watch` on one primary device
- rclone bisync detects state divergence and fails safely
**Safety nets in place:**
- Local process lock prevents concurrent runs on same device
- rclone bisync aborts if bucket state changed during sync
- S3 versioning recovers from any overwrites
- Documentation warns against multi-device `--watch`
**Failure mode:**
```bash
# Device A and B sync simultaneously
Device A: bm sync → succeeds
Device B: bm sync → "Error: path has changed, run --resync"
# User fixes with resync
Device B: bm sync --resync → establishes new baseline
```
**Decision:** Document the issue, add local lock, defer distributed coordination until users report actual problems.
### Cloud-Primary Conflict Model
**User mental model:**
- Cloud is the source of truth (like Dropbox/iCloud)
- Local is working copy
- On conflict: cloud wins, local edits → `.conflict` file
- User manually picks winner
**Why this works:**
- Simpler than bidirectional merge (no automatic resolution risk)
- Matches user expectations from Dropbox
- S3 versioning provides safety net for overwrites
- Clear recovery path: restore from S3 version if needed
**Example workflow:**
```bash
# Edit file on Device A and Device B while offline
# Both devices come online and sync
Device A: bm sync
# → Pushes to cloud first, becomes canonical version
Device B: bm sync
# → Detects conflict
# → Cloud version: work/notes.md
# → Local version: work/notes.md.conflict1
# → User manually merges or picks winner
# Restore if needed
bm cloud restore work/notes.md --version-id abc123
```
## Implementation Details
### 1. Local Process Lock
```python
# ~/.basic-memory/sync.lock
import os
import psutil
from pathlib import Path
class SyncLock:
def __init__(self):
self.lock_file = Path.home() / '.basic-memory' / 'sync.lock'
def acquire(self):
if self.lock_file.exists():
pid = int(self.lock_file.read_text())
if psutil.pid_exists(pid):
raise BisyncError(
f"Sync already running (PID {pid}). "
f"Wait for completion or kill stale process."
)
# Stale lock, remove it
self.lock_file.unlink()
self.lock_file.write_text(str(os.getpid()))
def release(self):
if self.lock_file.exists():
self.lock_file.unlink()
def __enter__(self):
self.acquire()
return self
def __exit__(self, *args):
self.release()
# Usage
with SyncLock():
run_rclone_bisync()
```
### 3. Sync Report Parsing
```python
# Parse rclone bisync output
import json
from datetime import datetime
from pathlib import Path
def parse_sync_report(rclone_output: str, duration: float, exit_code: int) -> dict:
"""Parse rclone bisync output into structured report."""
# rclone bisync outputs lines like:
# "Synching Path1 /local/path with Path2 remote:bucket"
# "- Path1 File was copied to Path2"
# "Bisync successful"
report = {
"timestamp": datetime.now().isoformat(),
"duration_seconds": duration,
"exit_code": exit_code,
"success": exit_code == 0,
"files_created": 0,
"files_updated": 0,
"files_deleted": 0,
"conflicts": [],
"errors": []
}
for line in rclone_output.split('\n'):
if 'was copied to' in line:
report['files_created'] += 1
elif 'was updated in' in line:
report['files_updated'] += 1
elif 'was deleted from' in line:
report['files_deleted'] += 1
elif '.conflict' in line:
report['conflicts'].append(line.strip())
elif 'ERROR' in line:
report['errors'].append(line.strip())
return report
def save_sync_report(report: dict):
"""Save sync report to history."""
history_dir = Path.home() / '.basic-memory' / 'sync-history'
history_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d-%H%M%S')
report_file = history_dir / f'{timestamp}.json'
report_file.write_text(json.dumps(report, indent=2))
# Usage in run_bisync()
start_time = time.time()
result = subprocess.run(bisync_cmd, capture_output=True, text=True)
duration = time.time() - start_time
report = parse_sync_report(result.stdout, duration, result.returncode)
save_sync_report(report)
if report['conflicts']:
console.print(f"[yellow]⚠ {len(report['conflicts'])} conflict(s) detected[/yellow]")
console.print("[dim]Run 'bm conflicts list' to view[/dim]")
```
### 4. User Commands
```bash
# View sync history
bm sync history
# → Lists recent syncs from ~/.basic-memory/sync-history/*.json
# → Shows: timestamp, duration, files changed, conflicts, errors
# View current conflicts
bm conflicts list
# → Scans sync directory for *.conflict* files
# → Shows: file path, conflict versions, timestamps
# Restore from S3 version
bm cloud restore work/notes.md --version-id abc123
# → Uses aws s3api get-object with version-id
# → Downloads to original path
bm cloud restore work/notes.md --timestamp "2025-10-03 14:30"
# → Lists versions, finds closest to timestamp
# → Downloads that version
# List file versions
bm cloud versions work/notes.md
# → Uses aws s3api list-object-versions
# → Shows: version-id, timestamp, size, author
# Interactive conflict resolution
bm conflicts resolve work/notes.md
# → Shows both versions side-by-side
# → Prompts: Keep local, keep cloud, merge manually, restore from S3 version
# → Cleans up .conflict files after resolution
```
## Success Metrics & Monitoring
**Phase 1 (v1) - Basic Safety:**
- [ ] Conflict detection rate < 5% of syncs (measure in telemetry)
- [ ] User can resolve conflicts within 5 minutes (UX testing)
- [ ] Documentation prevents 90% of multi-device issues
**Phase 2 (v2) - Observability:**
- [ ] 80% of users check `bm sync history` when troubleshooting
- [ ] Average time to restore from S3 version < 2 minutes
-
- [ ] Conflict resolution success rate > 95%
**What to measure:**
```python
# Telemetry in sync reports
{
"conflict_rate": conflicts / total_syncs,
"multi_device_collisions": count_state_divergence_errors,
"version_restores": count_restore_operations,
"avg_sync_duration": sum(durations) / count,
"max_delete_trips": count_max_delete_aborts
}
```
**When to add distributed lease:**
- Multi-device collision rate > 5% of syncs
- User complaints about state divergence errors
- Evidence that local lock isn't sufficient
**When to revisit Git (SPEC-14):**
- Teams feature launches (multi-user collaboration)
- Users request commit messages / audit trail
- PR-based review workflow becomes valuable
## Links
- SPEC-9: `specs/spec-9-multi-project-bisync`
- SPEC-14: `specs/spec-14-cloud-git-versioning` (deferred in favor of S3 versioning)
- rclone bisync docs: https://rclone.org/bisync/
- Tigris S3 versioning: https://www.tigrisdata.com/docs/buckets/versioning/
---
**Owner:** <assign> | **Review cadence:** weekly in standup | **Last updated:** 2025-10-03
+5 -1
View File
@@ -1,3 +1,7 @@
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
__version__ = "0.10.0"
# Package version - updated by release automation
__version__ = "0.15.0"
# API version for FastAPI - independent of package version
__api_version__ = "v0"
+4 -2
View File
@@ -8,17 +8,19 @@ from sqlalchemy import pool
from alembic import context
from basic_memory.models import Base
from basic_memory.config import ConfigManager
# 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 config as app_config
# Import after setting environment variable # noqa: E402
from basic_memory.models import Base # noqa: E402
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
app_config = ConfigManager().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)
@@ -0,0 +1,108 @@
"""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 ###
@@ -0,0 +1,104 @@
"""project constraint fix
Revision ID: 647e7a75e2cd
Revises: 5fe1ab1ccebe
Create Date: 2025-06-03 12:48:30.162566
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "647e7a75e2cd"
down_revision: Union[str, None] = "5fe1ab1ccebe"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Remove the problematic UNIQUE constraint on is_default column.
The UNIQUE constraint prevents multiple projects from having is_default=FALSE,
which breaks project creation when the service sets is_default=False.
Since SQLite doesn't support dropping specific constraints easily, we'll
recreate the table without the problematic constraint.
"""
# For SQLite, we need to recreate the table without the UNIQUE constraint
# Create a new table without the UNIQUE constraint on is_default
op.create_table(
"project_new",
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), # No UNIQUE constraint!
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
sa.UniqueConstraint("permalink"),
)
# Copy data from old table to new table
op.execute("INSERT INTO project_new SELECT * FROM project")
# Drop the old table
op.drop_table("project")
# Rename the new table
op.rename_table("project_new", "project")
# Recreate the indexes
with op.batch_alter_table("project", schema=None) as batch_op:
batch_op.create_index("ix_project_created_at", ["created_at"], unique=False)
batch_op.create_index("ix_project_name", ["name"], unique=True)
batch_op.create_index("ix_project_path", ["path"], unique=False)
batch_op.create_index("ix_project_permalink", ["permalink"], unique=True)
batch_op.create_index("ix_project_updated_at", ["updated_at"], unique=False)
def downgrade() -> None:
"""Add back the UNIQUE constraint on is_default column.
WARNING: This will break project creation again if multiple projects
have is_default=FALSE.
"""
# Recreate the table with the UNIQUE constraint
op.create_table(
"project_old",
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"), # Add back the problematic constraint
sa.UniqueConstraint("name"),
sa.UniqueConstraint("permalink"),
)
# Copy data (this may fail if multiple FALSE values exist)
op.execute("INSERT INTO project_old SELECT * FROM project")
# Drop the current table and rename
op.drop_table("project")
op.rename_table("project_old", "project")
# Recreate indexes
with op.batch_alter_table("project", schema=None) as batch_op:
batch_op.create_index("ix_project_created_at", ["created_at"], unique=False)
batch_op.create_index("ix_project_name", ["name"], unique=True)
batch_op.create_index("ix_project_path", ["path"], unique=False)
batch_op.create_index("ix_project_permalink", ["permalink"], unique=True)
batch_op.create_index("ix_project_updated_at", ["updated_at"], unique=False)
@@ -0,0 +1,49 @@
"""fix project foreign keys
Revision ID: a1b2c3d4e5f6
Revises: 647e7a75e2cd
Create Date: 2025-08-19 22:06:00.000000
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a1b2c3d4e5f6"
down_revision: Union[str, None] = "647e7a75e2cd"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Re-establish foreign key constraints that were lost during project table recreation.
The migration 647e7a75e2cd recreated the project table but did not re-establish
the foreign key constraint from entity.project_id to project.id, causing
foreign key constraint failures when trying to delete projects with related entities.
"""
# SQLite doesn't allow adding foreign key constraints to existing tables easily
# We need to be careful and handle the case where the constraint might already exist
with op.batch_alter_table("entity", schema=None) as batch_op:
# Try to drop existing foreign key constraint (may not exist)
try:
batch_op.drop_constraint("fk_entity_project_id", type_="foreignkey")
except Exception:
# Constraint may not exist, which is fine - we'll create it next
pass
# Add the foreign key constraint with CASCADE DELETE
# This ensures that when a project is deleted, all related entities are also deleted
batch_op.create_foreign_key(
"fk_entity_project_id", "project", ["project_id"], ["id"], ondelete="CASCADE"
)
def downgrade() -> None:
"""Remove the foreign key constraint."""
with op.batch_alter_table("entity", schema=None) as batch_op:
batch_op.drop_constraint("fk_entity_project_id", type_="foreignkey")
@@ -56,12 +56,6 @@ def upgrade() -> None:
);
""")
# Print instruction to manually reindex after migration
print("\n------------------------------------------------------------------")
print("IMPORTANT: After migration completes, manually run the reindex command:")
print("basic-memory sync")
print("------------------------------------------------------------------\n")
def downgrade() -> None:
"""Downgrade database schema to use old search index."""
+57 -10
View File
@@ -1,22 +1,60 @@
"""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.config import config as app_config
from basic_memory.api.routers import knowledge, search, memory, resource, project_info
from basic_memory.api.routers import (
directory_router,
importer_router,
knowledge,
management,
memory,
project,
resource,
search,
prompt_router,
)
from basic_memory.config import ConfigManager
from basic_memory.services.initialization import initialize_file_sync, initialize_app
@asynccontextmanager
async def lifespan(app: FastAPI): # pragma: no cover
"""Lifecycle manager for the FastAPI app."""
await db.run_migrations(app_config)
"""Lifecycle manager for the FastAPI app. Not called in stdio mcp mode"""
app_config = ConfigManager().config
logger.info("Starting Basic Memory API")
await initialize_app(app_config)
# Cache database connections in app state for performance
logger.info("Initializing database and caching connections...")
engine, session_maker = await db.get_or_create_db(app_config.database_path)
app.state.engine = engine
app.state.session_maker = session_maker
logger.info("Database connections cached in app state")
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()
@@ -24,17 +62,26 @@ async def lifespan(app: FastAPI): # pragma: no cover
app = FastAPI(
title="Basic Memory API",
description="Knowledge graph API for basic-memory",
version="0.1.0",
version=version,
lifespan=lifespan,
)
# Include routers
app.include_router(knowledge.router)
app.include_router(search.router)
app.include_router(memory.router)
app.include_router(resource.router)
app.include_router(project_info.router)
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)
+4 -2
View File
@@ -1,9 +1,11 @@
"""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 project_info_router as project_info
from . import prompt_router as prompt
__all__ = ["knowledge", "memory", "resource", "search", "project_info"]
__all__ = ["knowledge", "management", "memory", "project", "resource", "search", "prompt"]
@@ -0,0 +1,63 @@
"""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
@@ -0,0 +1,152 @@
"""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)}",
)
+156 -37
View File
@@ -10,6 +10,11 @@ from basic_memory.deps import (
get_search_service,
SearchServiceDep,
LinkResolverDep,
ProjectPathDep,
FileServiceDep,
ProjectConfigDep,
AppConfigDep,
SyncServiceDep,
)
from basic_memory.schemas import (
EntityListResponse,
@@ -17,11 +22,31 @@ from basic_memory.schemas import (
DeleteEntitiesResponse,
DeleteEntitiesRequest,
)
from basic_memory.schemas.request import EditEntityRequest, MoveEntityRequest
from basic_memory.schemas.base import Permalink, Entity
from basic_memory.services.exceptions import EntityNotFoundError
router = APIRouter(prefix="/knowledge", tags=["knowledge"])
async def resolve_relations_background(sync_service, entity_id: int, entity_permalink: str) -> None:
"""Background task to resolve relations for a specific entity.
This runs asynchronously after the API response is sent, preventing
long delays when creating entities with many relations.
"""
try:
# Only resolve relations for the newly created entity
await sync_service.resolve_relations(entity_id=entity_id)
logger.debug(
f"Background: Resolved relations for entity {entity_permalink} (id={entity_id})"
)
except Exception as e:
# Log but don't fail - this is a background task
logger.warning(
f"Background: Failed to resolve relations for entity {entity_permalink}: {e}"
)
## Create endpoints
@@ -44,43 +69,37 @@ async def create_entity(
result = EntityResponse.model_validate(entity)
logger.info(
"API response",
endpoint="create_entity",
title=result.title,
permalink=result.permalink,
status_code=201,
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,
sync_service: SyncServiceDep,
) -> EntityResponse:
"""Create or update an entity. If entity exists, it will be updated, otherwise created."""
logger.info(
"API request",
endpoint="create_or_update_entity",
permalink=permalink,
entity_type=data.entity_type,
title=data.title,
f"API request: create_or_update_entity for {project=}, {permalink=}, {data.entity_type=}, {data.title=}"
)
# Validate permalink matches
if data.permalink != permalink:
logger.warning(
"API validation error",
endpoint="create_or_update_entity",
permalink=permalink,
data_permalink=data.permalink,
error="Permalink mismatch",
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}'",
)
raise HTTPException(status_code=400, detail="Entity permalink must match URL path")
# Try create_or_update operation
entity, created = await entity_service.create_or_update_entity(data)
@@ -88,41 +107,141 @@ async def create_or_update_entity(
# reindex
await search_service.index_entity(entity, background_tasks=background_tasks)
# Schedule relation resolution as a background task for new entities
# This prevents blocking the API response while resolving potentially many relations
if created:
background_tasks.add_task(
resolve_relations_background, sync_service, entity.id, entity.permalink or ""
)
result = EntityResponse.model_validate(entity)
logger.info(
"API response",
endpoint="create_or_update_entity",
title=result.title,
permalink=result.permalink,
created=created,
status_code=response.status_code,
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/{permalink:path}", response_model=EntityResponse)
@router.get("/entities/{identifier:path}", response_model=EntityResponse)
async def get_entity(
entity_service: EntityServiceDep,
permalink: str,
link_resolver: LinkResolverDep,
identifier: str,
) -> EntityResponse:
"""Get a specific entity by ID.
"""Get a specific entity by file path or permalink..
Args:
permalink: Entity path ID
content: If True, include full file content
identifier: Entity file path or permalink
:param entity_service: EntityService
:param link_resolver: LinkResolver
"""
logger.info(f"request: get_entity with permalink={permalink}")
try:
entity = await entity_service.get_by_permalink(permalink)
result = EntityResponse.model_validate(entity)
return result
except EntityNotFoundError:
raise HTTPException(status_code=404, detail=f"Entity with {permalink} not found")
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)
@@ -161,8 +280,8 @@ async def delete_entity(
# Delete the entity
deleted = await entity_service.delete_entity(entity.permalink or entity.id)
# Remove from search index
background_tasks.add_task(search_service.delete_by_permalink, entity.permalink)
# Remove from search index (entity, observations, and relations)
background_tasks.add_task(search_service.handle_delete, entity)
result = DeleteEntitiesResponse(deleted=deleted)
return result
@@ -0,0 +1,80 @@
"""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 ConfigManager
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)
app_config = ConfigManager().config
# 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)
+6 -62
View File
@@ -1,78 +1,22 @@
"""Routes for memory:// URI operations."""
from typing import Annotated
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.repository import EntityRepository
from basic_memory.repository.search_repository import SearchIndexRow
from basic_memory.schemas.base import TimeFrame
from basic_memory.schemas.base import TimeFrame, parse_timeframe
from basic_memory.schemas.memory import (
GraphContext,
RelationSummary,
EntitySummary,
ObservationSummary,
MemoryMetadata,
normalize_memory_url,
)
from basic_memory.schemas.search import SearchItemType
from basic_memory.services.context_service import ContextResultRow
from basic_memory.api.routers.utils import to_graph_context
router = APIRouter(prefix="/memory", tags=["memory"])
async def to_graph_context(context, entity_repository: EntityRepository, page: int, page_size: int):
# return results
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.type,
from_entity=from_entity.permalink, # pyright: ignore
to_entity=to_entity.permalink if to_entity else None,
created_at=item.created_at,
)
case _: # pragma: no cover
raise ValueError(f"Unexpected type: {item.type}")
primary_results = [await to_summary(r) for r in context["primary_results"]]
related_results = [await to_summary(r) for r in context["related_results"]]
metadata = MemoryMetadata.model_validate(context["metadata"])
# Transform to GraphContext
return GraphContext(
primary_results=primary_results,
related_results=related_results,
metadata=metadata,
page=page,
page_size=page_size,
)
@router.get("/recent", response_model=GraphContext)
async def recent(
context_service: ContextServiceDep,
@@ -95,7 +39,7 @@ async def recent(
f"Getting recent context: `{types}` depth: `{depth}` timeframe: `{timeframe}` page: `{page}` page_size: `{page_size}` max_related: `{max_related}`"
)
# Parse timeframe
since = parse(timeframe)
since = parse_timeframe(timeframe)
limit = page_size
offset = (page - 1) * page_size
@@ -119,7 +63,7 @@ async def get_memory_context(
entity_repository: EntityRepositoryDep,
uri: str,
depth: int = 1,
timeframe: TimeFrame = "7d",
timeframe: Optional[TimeFrame] = None,
page: int = 1,
page_size: int = 10,
max_related: int = 10,
@@ -133,7 +77,7 @@ async def get_memory_context(
memory_url = normalize_memory_url(uri)
# Parse timeframe
since = parse(timeframe)
since = parse_timeframe(timeframe) if timeframe else None
limit = page_size
offset = (page - 1) * page_size
@@ -1,274 +0,0 @@
"""Router for statistics and system information."""
import json
from datetime import datetime
from basic_memory.config import config, config_manager
from basic_memory.deps import (
ProjectInfoRepositoryDep,
)
from basic_memory.repository.project_info_repository import ProjectInfoRepository
from basic_memory.schemas import (
ProjectInfoResponse,
ProjectStatistics,
ActivityMetrics,
SystemStatus,
)
from basic_memory.sync.watch_service import WATCH_STATUS_JSON
from fastapi import APIRouter
from sqlalchemy import text
router = APIRouter(prefix="/stats", tags=["statistics"])
@router.get("/project-info", response_model=ProjectInfoResponse)
async def get_project_info(
repository: ProjectInfoRepositoryDep,
) -> ProjectInfoResponse:
"""Get comprehensive information about the current Basic Memory project."""
# Get statistics
statistics = await get_statistics(repository)
# Get activity metrics
activity = await get_activity_metrics(repository)
# Get system status
system = await get_system_status()
# Get project configuration information
project_name = config.project
project_path = str(config.home)
available_projects = config_manager.projects
default_project = config_manager.default_project
# Construct the response
return ProjectInfoResponse(
project_name=project_name,
project_path=project_path,
available_projects=available_projects,
default_project=default_project,
statistics=statistics,
activity=activity,
system=system,
)
async def get_statistics(repository: ProjectInfoRepository) -> ProjectStatistics:
"""Get statistics about the current project."""
# Get basic counts
entity_count_result = await repository.execute_query(text("SELECT COUNT(*) FROM entity"))
total_entities = entity_count_result.scalar() or 0
observation_count_result = await repository.execute_query(
text("SELECT COUNT(*) FROM observation")
)
total_observations = observation_count_result.scalar() or 0
relation_count_result = await repository.execute_query(text("SELECT COUNT(*) FROM relation"))
total_relations = relation_count_result.scalar() or 0
unresolved_count_result = await repository.execute_query(
text("SELECT COUNT(*) FROM relation WHERE to_id IS NULL")
)
total_unresolved = unresolved_count_result.scalar() or 0
# Get entity counts by type
entity_types_result = await repository.execute_query(
text("SELECT entity_type, COUNT(*) FROM entity GROUP BY entity_type")
)
entity_types = {row[0]: row[1] for row in entity_types_result.fetchall()}
# Get observation counts by category
category_result = await repository.execute_query(
text("SELECT category, COUNT(*) FROM observation GROUP BY category")
)
observation_categories = {row[0]: row[1] for row in category_result.fetchall()}
# Get relation counts by type
relation_types_result = await repository.execute_query(
text("SELECT relation_type, COUNT(*) FROM relation GROUP BY relation_type")
)
relation_types = {row[0]: row[1] for row in relation_types_result.fetchall()}
# Find most connected entities (most outgoing relations)
connected_result = await repository.execute_query(
text("""
SELECT e.id, e.title, e.permalink, COUNT(r.id) AS relation_count
FROM entity e
JOIN relation r ON e.id = r.from_id
GROUP BY e.id
ORDER BY relation_count DESC
LIMIT 10
""")
)
most_connected = [
{"id": row[0], "title": row[1], "permalink": row[2], "relation_count": row[3]}
for row in connected_result.fetchall()
]
# Count isolated entities (no relations)
isolated_result = await repository.execute_query(
text("""
SELECT COUNT(e.id)
FROM entity e
LEFT JOIN relation r1 ON e.id = r1.from_id
LEFT JOIN relation r2 ON e.id = r2.to_id
WHERE r1.id IS NULL AND r2.id IS NULL
""")
)
isolated_count = isolated_result.scalar() or 0
return ProjectStatistics(
total_entities=total_entities,
total_observations=total_observations,
total_relations=total_relations,
total_unresolved_relations=total_unresolved,
entity_types=entity_types,
observation_categories=observation_categories,
relation_types=relation_types,
most_connected_entities=most_connected,
isolated_entities=isolated_count,
)
async def get_activity_metrics(repository: ProjectInfoRepository) -> ActivityMetrics:
"""Get activity metrics for the current project."""
# Get recently created entities
created_result = await repository.execute_query(
text("""
SELECT id, title, permalink, entity_type, created_at
FROM entity
ORDER BY created_at DESC
LIMIT 10
""")
)
recently_created = [
{
"id": row[0],
"title": row[1],
"permalink": row[2],
"entity_type": row[3],
"created_at": row[4],
}
for row in created_result.fetchall()
]
# Get recently updated entities
updated_result = await repository.execute_query(
text("""
SELECT id, title, permalink, entity_type, updated_at
FROM entity
ORDER BY updated_at DESC
LIMIT 10
""")
)
recently_updated = [
{
"id": row[0],
"title": row[1],
"permalink": row[2],
"entity_type": row[3],
"updated_at": row[4],
}
for row in updated_result.fetchall()
]
# Get monthly growth over the last 6 months
# Calculate the start of 6 months ago
now = datetime.now()
six_months_ago = datetime(
now.year - (1 if now.month <= 6 else 0), ((now.month - 6) % 12) or 12, 1
)
# Query for monthly entity creation
entity_growth_result = await repository.execute_query(
text(f"""
SELECT
strftime('%Y-%m', created_at) AS month,
COUNT(*) AS count
FROM entity
WHERE created_at >= '{six_months_ago.isoformat()}'
GROUP BY month
ORDER BY month
""")
)
entity_growth = {row[0]: row[1] for row in entity_growth_result.fetchall()}
# Query for monthly observation creation
observation_growth_result = await repository.execute_query(
text(f"""
SELECT
strftime('%Y-%m', created_at) AS month,
COUNT(*) AS count
FROM observation
INNER JOIN entity ON observation.entity_id = entity.id
WHERE entity.created_at >= '{six_months_ago.isoformat()}'
GROUP BY month
ORDER BY month
""")
)
observation_growth = {row[0]: row[1] for row in observation_growth_result.fetchall()}
# Query for monthly relation creation
relation_growth_result = await repository.execute_query(
text(f"""
SELECT
strftime('%Y-%m', created_at) AS month,
COUNT(*) AS count
FROM relation
INNER JOIN entity ON relation.from_id = entity.id
WHERE entity.created_at >= '{six_months_ago.isoformat()}'
GROUP BY month
ORDER BY month
""")
)
relation_growth = {row[0]: row[1] for row in relation_growth_result.fetchall()}
# Combine all monthly growth data
monthly_growth = {}
for month in set(
list(entity_growth.keys()) + list(observation_growth.keys()) + list(relation_growth.keys())
):
monthly_growth[month] = {
"entities": entity_growth.get(month, 0),
"observations": observation_growth.get(month, 0),
"relations": relation_growth.get(month, 0),
"total": (
entity_growth.get(month, 0)
+ observation_growth.get(month, 0)
+ relation_growth.get(month, 0)
),
}
return ActivityMetrics(
recently_created=recently_created,
recently_updated=recently_updated,
monthly_growth=monthly_growth,
)
async def get_system_status() -> SystemStatus:
"""Get system status information."""
import basic_memory
# Get database information
db_path = config.database_path
db_size = db_path.stat().st_size if db_path.exists() else 0
db_size_readable = f"{db_size / (1024 * 1024):.2f} MB"
# Get watch service status if available
watch_status = None
watch_status_path = config.home / ".basic-memory" / WATCH_STATUS_JSON
if watch_status_path.exists():
try:
watch_status = json.loads(watch_status_path.read_text(encoding="utf-8"))
except Exception: # pragma: no cover
pass
return SystemStatus(
version=basic_memory.__version__,
database_path=str(db_path),
database_size=db_size_readable,
watch_status=watch_status,
timestamp=datetime.now(),
)
@@ -0,0 +1,337 @@
"""Router for project management."""
import os
from fastapi import APIRouter, HTTPException, Path, Body, BackgroundTasks
from typing import Optional
from loguru import logger
from basic_memory.deps import (
ProjectConfigDep,
ProjectServiceDep,
ProjectPathDep,
SyncServiceDep,
)
from basic_memory.schemas import ProjectInfoResponse, SyncReportResponse
from basic_memory.schemas.project_info import (
ProjectList,
ProjectItem,
ProjectInfoRequest,
ProjectStatusResponse,
)
# Router for resources in a specific project
# The ProjectPathDep is used in the path as a prefix, so the request path is like /{project}/project/info
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,
project: ProjectPathDep,
) -> ProjectInfoResponse:
"""Get comprehensive information about the specified Basic Memory project."""
return await project_service.get_project_info(project)
@project_router.get("/item", response_model=ProjectItem)
async def get_project(
project_service: ProjectServiceDep,
project: ProjectPathDep,
) -> ProjectItem:
"""Get bassic info about the specified Basic Memory project."""
found_project = await project_service.get_project(project)
if not found_project:
raise HTTPException(
status_code=404, detail=f"Project: '{project}' does not exist"
) # pragma: no cover
return ProjectItem(
name=found_project.name,
path=found_project.path,
is_default=found_project.is_default or False,
)
# Update a project
@project_router.patch("/{name}", response_model=ProjectStatusResponse)
async def update_project(
project_service: ProjectServiceDep,
name: str = Path(..., description="Name of the project to update"),
path: Optional[str] = Body(None, description="New absolute 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:
name: The name of the project to update
path: Optional new absolute path for the project
is_active: Optional status update for the project
Returns:
Response confirming the project was updated
"""
try:
# Validate that path is absolute if provided
if path and not os.path.isabs(path):
raise HTTPException(status_code=400, detail="Path must be absolute")
# Get original project info for the response
old_project_info = ProjectItem(
name=name,
path=project_service.projects.get(name, ""),
)
if path:
await project_service.move_project(name, path)
elif is_active is not None:
await project_service.update_project(name, is_active=is_active)
# Get updated project info
updated_path = path if path else project_service.projects.get(name, "")
return ProjectStatusResponse(
message=f"Project '{name}' updated successfully",
status="success",
default=(name == project_service.default_project),
old_project=old_project_info,
new_project=ProjectItem(name=name, path=updated_path),
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
# Sync project filesystem
@project_router.post("/sync")
async def sync_project(
background_tasks: BackgroundTasks,
sync_service: SyncServiceDep,
project_config: ProjectConfigDep,
):
"""Force project filesystem sync to database.
Scans the project directory and updates the database with any new or modified files.
Args:
background_tasks: FastAPI background tasks
sync_service: Sync service for this project
project_config: Project configuration
Returns:
Response confirming sync was initiated
"""
background_tasks.add_task(sync_service.sync, project_config.home, project_config.name)
logger.info(f"Filesystem sync initiated for project: {project_config.name}")
return {
"status": "sync_started",
"message": f"Filesystem sync initiated for project '{project_config.name}'",
}
@project_router.post("/status", response_model=SyncReportResponse)
async def project_sync_status(
sync_service: SyncServiceDep,
project_config: ProjectConfigDep,
) -> SyncReportResponse:
"""Scan directory for changes compared to database state.
Args:
sync_service: Sync service for this project
project_config: Project configuration
Returns:
Scan report with details on files that need syncing
"""
logger.info(f"Scanning filesystem for project: {project_config.name}")
sync_report = await sync_service.scan(project_config.home)
return SyncReportResponse.from_sync_report(sync_report)
# 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, set_default=project_data.set_default
)
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))
# Get the default project
@project_resource_router.get("/default", response_model=ProjectItem)
async def get_default_project(
project_service: ProjectServiceDep,
) -> ProjectItem:
"""Get the default project.
Returns:
Response with project default information
"""
# 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"
)
return ProjectItem(name=default_project.name, path=default_project.path, is_default=True)
# Synchronize projects between config and database
@project_resource_router.post("/config/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))

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