Compare commits
586 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a026412a13 | |||
| f23dd0474b | |||
| fced804438 | |||
| 1fdc9fdc69 | |||
| 2feecdfaf7 | |||
| 195229f78e | |||
| d15f6a8427 | |||
| b8a3a14ad2 | |||
| 9b199c6dcb | |||
| fe4a7b1622 | |||
| 7d2012a82c | |||
| 7f2d4d2a6f | |||
| 8b7f39ee9b | |||
| a368d06fd2 | |||
| 3a2b80b7e9 | |||
| bd5099120f | |||
| 8e64caf784 | |||
| ccb5740920 | |||
| 373893a7ee | |||
| 1987581ee3 | |||
| 59134affa4 | |||
| c7d97decd6 | |||
| 5d5efa02a5 | |||
| 4f12182e28 | |||
| 2c5b63c4a2 | |||
| c4c9f842ea | |||
| dc0ca71c18 | |||
| 236ae268aa | |||
| bd5923a370 | |||
| 4ea5396ddd | |||
| e97eafa55a | |||
| 254e30423d | |||
| 496af07ced | |||
| b5667f9b55 | |||
| 54b968b93c | |||
| e59b5cb6d9 | |||
| f0335b998e | |||
| 66effb04a7 | |||
| 9331126ba1 | |||
| 3bbb44af0b | |||
| f9b2a075a9 | |||
| 0a3f3f07f8 | |||
| 184ea6d9fa | |||
| f5a0e942b0 | |||
| e46555bf2c | |||
| 74e6afdc0f | |||
| aa635b8a8b | |||
| 1856d4b462 | |||
| 73413486bc | |||
| b09eca1698 | |||
| 3004d0d1fe | |||
| b6369d3d14 | |||
| db6d0dcd9e | |||
| 5a5eb443ea | |||
| e1df23d793 | |||
| 0462a7d4ba | |||
| 79db876bd6 | |||
| 0130573342 | |||
| c372dfb09f | |||
| d763d86798 | |||
| 538af97cba | |||
| 054178d155 | |||
| 18f00f861d | |||
| da4d369c32 | |||
| 0f3889fdd0 | |||
| c44291830c | |||
| e1cccba72d | |||
| 6ff39076a0 | |||
| 56cefbaafd | |||
| 3337c7d1ff | |||
| e248763a73 | |||
| 2e5813d31e | |||
| 2cde8d2659 | |||
| 9515130b2a | |||
| b86dd6fb53 | |||
| 8451f2b1d7 | |||
| ee0397513d | |||
| 9c9ff2931d | |||
| bbe6c1e8f3 | |||
| edb7991ccf | |||
| d7faeb754e | |||
| deef89a724 | |||
| 30499a9f61 | |||
| 1ac65b944c | |||
| 306e562281 | |||
| ed82b0c417 | |||
| 0cb3f95d67 | |||
| 0a36256f8a | |||
| 36e67e6eec | |||
| f2683291e4 | |||
| 8c05a9ec80 | |||
| a6d8d4c0f6 | |||
| 0239f4abb4 | |||
| 9259a7eb59 | |||
| 55d675e278 | |||
| 6afe4fd0cc | |||
| 113d1b6f1b | |||
| 545804f194 | |||
| 8bc03d1357 | |||
| f6e0a5b5bb | |||
| 7624a20d8d | |||
| d84708ca7f | |||
| 1428d18de1 | |||
| 312662f382 | |||
| ed9487708e | |||
| 8df88e4d02 | |||
| 07778790d3 | |||
| b609c4e531 | |||
| f1a065bce3 | |||
| 2b94d9a278 | |||
| 344e651693 | |||
| c97733d785 | |||
| 00537272c6 | |||
| b057912452 | |||
| 8489a3d37e | |||
| a47c9c021f | |||
| c46d7a6833 | |||
| 343a6e118b | |||
| a0e754b7ae | |||
| 24ca5f6804 | |||
| f1d50c2ba7 | |||
| 8072449a78 | |||
| 45d3f58e4d | |||
| d9c8923148 | |||
| 15bd6b95ef | |||
| 0715dcff3d | |||
| 009e84926d | |||
| 8838571509 | |||
| 530cbac73f | |||
| e3ced49d9d | |||
| 8f962fdd87 | |||
| fbb497f6dc | |||
| 0023e736ab | |||
| 0b2080114b | |||
| 8730067f3a | |||
| e14ba92631 | |||
| 9d98892570 | |||
| 3be4495723 | |||
| 17c0e0a29b | |||
| 7ebf16a95d | |||
| c05075f8d4 | |||
| 4cef9281ca | |||
| 6888effef2 | |||
| 38616c345d | |||
| f3c1aa895c | |||
| d978aba09b | |||
| 2aaee734c9 | |||
| 369ad37b3d | |||
| 4e5f701d22 | |||
| 9d9ea4d61c | |||
| 7a502e6474 | |||
| c7835a9d5c | |||
| 85835ae533 | |||
| 671e3d4db9 | |||
| e11aeff8d9 | |||
| 803f3efe53 | |||
| d6dab8552c | |||
| d1d433df15 | |||
| 1799c94953 | |||
| 07996181b3 | |||
| a1c37c1dba | |||
| aff53cca93 | |||
| 863e0a4e24 | |||
| eeeade4f07 | |||
| 03793eaf7c | |||
| 26f7e98932 | |||
| 5947f04bd3 | |||
| ba1439fefc | |||
| ef411ceb12 | |||
| c6baf58aa7 | |||
| 3c1748cc89 | |||
| 9206e7960a | |||
| 53c4c20d22 | |||
| b4486d20bd | |||
| a4000f64ce | |||
| 88a1778798 | |||
| 4ce21984a4 | |||
| eb7fbaf0bf | |||
| 8adf1f4ed4 | |||
| 45ce1813e4 | |||
| 2744c4b6a5 | |||
| fd732aa6fe | |||
| 537e58ad7d | |||
| 48e6e84beb | |||
| 02c14acddb | |||
| 0b5425f163 | |||
| 0bcda4a14a | |||
| 7a49f57dee | |||
| 58db2817d2 | |||
| 98fbd60527 | |||
| 6281a81256 | |||
| be1d0b169f | |||
| 148bf6f75a | |||
| 272a983709 | |||
| ef7adb7b99 | |||
| 3cd9178415 | |||
| 856737fe3c | |||
| 1fd680c3f1 | |||
| 38919d11cb | |||
| 85684f848f | |||
| 14ce5a3bd0 | |||
| 45d6caf723 | |||
| 1652f862dd | |||
| c23927d124 | |||
| 1a74d85973 | |||
| d71c6e8568 | |||
| 63b98491be | |||
| 622d37e4a8 | |||
| 916baf8971 | |||
| 95937c6d0a | |||
| 24dc9a2931 | |||
| 85c63e5a7a | |||
| f227ef6a86 | |||
| 897b1edaa4 | |||
| 0c12a39a98 | |||
| efbc758325 | |||
| a0f20eb102 | |||
| 78673d8e51 | |||
| 126c0495c0 | |||
| 4a43d7df4a | |||
| c462faf046 | |||
| 70bb10be1d | |||
| fbf9045d78 | |||
| 1094210c52 | |||
| 391feb639f | |||
| a920a9ff29 | |||
| 05efe8701c | |||
| 0eaf30bb06 | |||
| 0818bda565 | |||
| 6f99d2e551 | |||
| 73d940e064 | |||
| c3678a11d2 | |||
| 203d684c24 | |||
| a872220924 | |||
| 7d763a66ff | |||
| b5d4fb559c | |||
| 830775276d | |||
| ed894fc3ed | |||
| 704338edcf | |||
| 0ca02a7ebe | |||
| 28cc5225a7 | |||
| 9b7bbc7116 | |||
| 138c283d6c | |||
| 7a8954c37e | |||
| 10c7c19c03 | |||
| fb5e9e1d77 | |||
| 66b91b2847 | |||
| b004565df9 | |||
| a258b73e1d | |||
| 9a845f2906 | |||
| 6517e9845f | |||
| 1af05392ee | |||
| cad7019c89 | |||
| 099c334e3d | |||
| 7685586178 | |||
| e9d0a944a9 | |||
| caf3c14bb1 | |||
| c5d9067754 | |||
| e0fc59ea97 | |||
| 49b2adc35c | |||
| 1646572f69 | |||
| f0d7398815 | |||
| 581b7b17c6 | |||
| d775f7bab9 | |||
| fc01f6abaf | |||
| 4614fd09d5 | |||
| 0d4ad7bbf0 | |||
| 7ccec7eba2 | |||
| 021af74545 | |||
| 2ad0ee9d5d | |||
| c9946ecf1e | |||
| 0ba6f219f1 | |||
| 0b3272ae6e | |||
| a7d7cc5ee6 | |||
| a7e696b039 | |||
| 8aaddb6d45 | |||
| d7565312fc | |||
| c7e6eab02f | |||
| bb8da31472 | |||
| e78345ff25 | |||
| 32236cd247 | |||
| 4fd6d0c648 | |||
| e6c8e3662c | |||
| 449b62d947 | |||
| b7497d7484 | |||
| d1431bdb1b | |||
| 171bef717f | |||
| 729a5a3b8d | |||
| 434cdf24dd | |||
| 7f9c1a97a4 | |||
| 53fb13b054 | |||
| bd6c8348b8 | |||
| 994c8b8e7e | |||
| a78e8c3ac5 | |||
| 53900c5baa | |||
| 02c6de3387 | |||
| 9ccf4b6b56 | |||
| ba74ca7e18 | |||
| 5258f45730 | |||
| e773c002ce | |||
| e70ba944e7 | |||
| e41579f971 | |||
| 2b7008d997 | |||
| 56e5cc072b | |||
| c0538ad2dd | |||
| 962d88ea43 | |||
| cd5efd4a44 | |||
| 00b73b0d08 | |||
| a09066e0f0 | |||
| be352ab474 | |||
| 8d2e70cfc8 | |||
| 53438d1eab | |||
| 032de7e3f2 | |||
| fd2b188645 | |||
| 453cba94e4 | |||
| 48cb4be4cd | |||
| 3e876a7549 | |||
| 1fa93ecbd2 | |||
| 73202d1aab | |||
| 795e339333 | |||
| 07e304ce8e | |||
| 2a1c06d9ad | |||
| c6f93a0294 | |||
| ccc4386627 | |||
| 7616b2bb08 | |||
| 14c1fe4e89 | |||
| 367dc6962a | |||
| ee18eb2fea | |||
| 2a050edee4 | |||
| f3b1945e4c | |||
| 16d7eddbf7 | |||
| f5a11f3911 | |||
| ee83b0e5a8 | |||
| a7bf42ef49 | |||
| 903591384d | |||
| 33ee1e0831 | |||
| c83d567917 | |||
| ace6a0f50d | |||
| fc38877008 | |||
| 99a35a7fb4 | |||
| ea2e93d926 | |||
| 324844a670 | |||
| f818702ab7 | |||
| 2efd8f44e2 | |||
| 5da97e4820 | |||
| 17a6733c9d | |||
| 3e168b98f3 | |||
| 1091e11322 | |||
| f40ab31685 | |||
| bcf7f40979 | |||
| 8c7e29e325 | |||
| 84c0b36dee | |||
| 2c5c606a39 | |||
| a1d7792bdb | |||
| 7979b4192e | |||
| 52d9b3c752 | |||
| e0d8aeb149 | |||
| 17b929446a | |||
| b00e4ff5a1 | |||
| 0499319ded | |||
| 3a6baf80fc | |||
| ec2fa07350 | |||
| 73cade27ab | |||
| 7e024a8674 | |||
| 22f7bfa398 | |||
| cd7cee650f | |||
| 105bcaa025 | |||
| 74e12eb782 | |||
| 7a8b08d11e | |||
| 9aa40246a8 | |||
| 7aff836c57 | |||
| 285e96baea | |||
| 2cd2a62f30 | |||
| f3d8d8d617 | |||
| 9743fcd13e | |||
| 65d1984a53 | |||
| b814d40ab1 | |||
| 2438094914 | |||
| 5d74d7407c | |||
| b6aeb3217c | |||
| 08ee7e1201 | |||
| 63ae9ee0e4 | |||
| 0e78751d34 | |||
| 59eae34dee | |||
| b1e55e169e | |||
| 173bff35c1 | |||
| 629c8e47c9 | |||
| 9e4b8bca8f | |||
| b0cc559426 | |||
| 7460a938df | |||
| 43fa5762a8 | |||
| fb1350b294 | |||
| 7585a29c96 | |||
| 752c78c379 | |||
| a4a3b1b689 | |||
| 6361574a20 | |||
| 24a1d6195d | |||
| a0cf62375d | |||
| 473f70c949 | |||
| 2c29dcc2b2 | |||
| 448210e552 | |||
| 3621bb7b4d | |||
| f80ac0ee72 | |||
| 23ddf1918c | |||
| 2aca19aa05 | |||
| 827f7cf3e3 | |||
| bd4f55158b | |||
| 5360005122 | |||
| 39f811f8b5 | |||
| 8e69c8b533 | |||
| 627a5c3c22 | |||
| cd88945b22 | |||
| cd8e372f0a | |||
| a589f8b894 | |||
| c2f4b632cf | |||
| 46d102cef1 | |||
| 8e4dc026ce | |||
| 7af8e198c2 | |||
| 12b51522bc | |||
| ac9e148bcc | |||
| 546e3cd8db | |||
| de4737cc22 | |||
| 77eefeb252 | |||
| e5923a0378 | |||
| 1bf348259b | |||
| 224e4bf9e4 | |||
| 9f1db23c78 | |||
| db5ef7d35c | |||
| f50650763d | |||
| 8a065c32f4 | |||
| 2a3adc109a | |||
| a52ce1c860 | |||
| 616c1f0710 | |||
| 74847cc380 | |||
| d3b6c85184 | |||
| af44941d5a | |||
| 35e4f73ae8 | |||
| 7be001ca68 | |||
| 3269a2f33a | |||
| b8191d090f | |||
| 2ce8a8e4b0 | |||
| f8099cd004 | |||
| 688e0b0971 | |||
| ed09ea4ec7 | |||
| c85d9f74d7 | |||
| 84d2aaf641 | |||
| 7789864493 | |||
| c6215fd819 | |||
| b4c26a6133 | |||
| 3fdce683d7 | |||
| 782cb2df28 | |||
| 56c875f137 | |||
| 5049de7e2d | |||
| 49011768f7 | |||
| bc3557f000 | |||
| 611f5cd305 | |||
| 4ea392d284 | |||
| d491757980 | |||
| 7a69ca2c36 | |||
| 70a6ce3411 | |||
| 5b69fd65cd | |||
| 85a178a6b8 | |||
| e4b32d7bc9 | |||
| 9590b934cf | |||
| 735f239f9b | |||
| 3ee30e1f36 | |||
| ac401ea254 | |||
| fb2fd62ed9 | |||
| 126d1655e6 | |||
| 2abf626c46 | |||
| ba8e3d112d | |||
| 7108a7baf1 | |||
| 35884ef3a7 | |||
| 040be05a81 | |||
| c141d7d1e6 | |||
| b73aeb5ed8 | |||
| 9a0e0bd82d | |||
| 117fa44ecf | |||
| 69d7610d47 | |||
| f608cd13f1 | |||
| 2162ad57fe | |||
| dd6ca80716 | |||
| ae3eeb0cc1 | |||
| 602c55fe90 | |||
| 91bfe2dc92 | |||
| a3cae1064d | |||
| c5c70cb0f4 | |||
| 80ec860a1c | |||
| f64d5b2152 | |||
| 69a625acd1 | |||
| 53c29a37ca | |||
| d8c13bf1d3 | |||
| 3f70f5ed42 | |||
| 569a3de80b | |||
| ac08a8d024 | |||
| c13d4b1511 | |||
| 5b85d33a99 | |||
| e2bd3142dc | |||
| 40b7de9168 | |||
| 455a280707 | |||
| 634486107b | |||
| 23d34289fd | |||
| 5ec4087f7c | |||
| 71941c35dd | |||
| 020957cd76 | |||
| b3af6aa685 | |||
| 9437a5f83b | |||
| 3c1cc346df | |||
| 81616ab42e | |||
| 321471f29d | |||
| 73ea91fe0d | |||
| 03d4e97b90 | |||
| 98622a7a47 | |||
| 54dfa08aba | |||
| 9433065a57 | |||
| 2934176331 | |||
| ac89eb47cb | |||
| 78a3412bcf | |||
| fa314b5d2b | |||
| d2ac62a27c | |||
| 7ce7a29f35 | |||
| 3f4d9e4d87 | |||
| 00c8633cfc | |||
| 617e60bda4 | |||
| 6c19c9edf5 | |||
| 9bff1f732e | |||
| 248214cb11 | |||
| 40ea28b0bf | |||
| 43cbb7b38c | |||
| 7930ddb291 | |||
| 1844e58210 | |||
| 7fbb5ebfc5 | |||
| 0e5a465b10 | |||
| 9d581cee13 | |||
| f58852954b | |||
| edd426b99f | |||
| 6b4a421315 | |||
| 4632405446 | |||
| 3a6d5ff58d | |||
| 03de85ec32 | |||
| 084c91ecce | |||
| 3d45227b72 | |||
| 069c0a21c6 | |||
| b27827671d | |||
| 0743ade5fc | |||
| f1c95709cb | |||
| 3c68b7d5dd | |||
| 3d0077990f | |||
| 731b502d36 | |||
| 2a881b1425 | |||
| 681af5d450 | |||
| b35037eb86 | |||
| b667bca5a2 | |||
| e716946b44 | |||
| 46c4fd2164 | |||
| 9c791259a0 | |||
| cc2cae72c1 | |||
| 0a1fdd8f15 | |||
| 78f234b180 | |||
| cbe72be10a | |||
| 0ade6b0603 | |||
| 390ff9d31c | |||
| 3ed74b4a4d | |||
| dfaf0fea9c | |||
| b26afa927f | |||
| 3806943f8c | |||
| 3bffb2e190 | |||
| 9fe34dcddf | |||
| eb5e4ec6bd | |||
| 6b110b28dd | |||
| 88d193e0aa | |||
| fa9175f049 | |||
| 7c787a413c | |||
| 0fa1adf3b5 | |||
| cbe488fbd2 | |||
| 99ff8f60c2 | |||
| 06ee2852a0 | |||
| 939bab1a24 | |||
| 67736487fb | |||
| 2ac620f98b | |||
| 4cbda7a9d4 | |||
| 3070ade69a | |||
| 9af913da4f | |||
| 7b7bec9557 | |||
| fea2f40d1b | |||
| eb1e7b6088 |
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,199 @@
|
||||
# /release - Create Stable Release
|
||||
|
||||
Create a stable release using the automated justfile target with comprehensive validation.
|
||||
|
||||
## Usage
|
||||
```
|
||||
/release <version>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `version` (required): Release version like `v0.13.2`
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert release manager for the Basic Memory project. When the user runs `/release`, execute the following steps:
|
||||
|
||||
### Step 1: Pre-flight Validation
|
||||
|
||||
#### Version Check
|
||||
1. Check current version in `src/basic_memory/__init__.py`
|
||||
2. Verify new version format matches `v\d+\.\d+\.\d+` pattern
|
||||
3. Confirm version is higher than current version
|
||||
|
||||
#### Git Status
|
||||
1. Check current git status for uncommitted changes
|
||||
2. Verify we're on the `main` branch
|
||||
3. Confirm no existing tag with this version
|
||||
|
||||
#### Documentation Validation
|
||||
1. **Changelog Check**
|
||||
- CHANGELOG.md contains entry for target version
|
||||
- Entry includes all major features and fixes
|
||||
- Breaking changes are documented
|
||||
|
||||
### Step 2: Use Justfile Automation
|
||||
Execute the automated release process:
|
||||
```bash
|
||||
just release <version>
|
||||
```
|
||||
|
||||
The justfile target handles:
|
||||
- ✅ Version format validation
|
||||
- ✅ Git status and branch checks
|
||||
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
|
||||
- ✅ Version update in `src/basic_memory/__init__.py`
|
||||
- ✅ Automatic commit with proper message
|
||||
- ✅ Tag creation and pushing to GitHub
|
||||
- ✅ Release workflow trigger (automatic on tag push)
|
||||
|
||||
The GitHub Actions workflow (`.github/workflows/release.yml`) then:
|
||||
- ✅ Builds the package using `uv build`
|
||||
- ✅ Creates GitHub release with auto-generated notes
|
||||
- ✅ Publishes to PyPI
|
||||
- ✅ Updates Homebrew formula (stable releases only)
|
||||
|
||||
### Step 3: Monitor Release Process
|
||||
1. Verify tag push triggered the workflow (should start automatically within seconds)
|
||||
2. Monitor workflow progress at: https://github.com/basicmachines-co/basic-memory/actions
|
||||
3. Watch for successful completion of both jobs:
|
||||
- `release` - Builds package and publishes to PyPI
|
||||
- `homebrew` - Updates Homebrew formula (stable releases only)
|
||||
4. Check for any workflow failures and investigate logs if needed
|
||||
|
||||
### Step 4: Post-Release Validation
|
||||
|
||||
#### GitHub Release
|
||||
1. Verify GitHub release is created at: https://github.com/basicmachines-co/basic-memory/releases/tag/<version>
|
||||
2. Check that release notes are auto-generated from commits
|
||||
3. Validate release assets (`.whl` and `.tar.gz` files are attached)
|
||||
|
||||
#### PyPI Publication
|
||||
1. Verify package published at: https://pypi.org/project/basic-memory/<version>/
|
||||
2. Test installation: `uv tool install basic-memory`
|
||||
3. Verify installed version: `basic-memory --version`
|
||||
|
||||
#### Homebrew Formula (Stable Releases Only)
|
||||
1. Check formula update at: https://github.com/basicmachines-co/homebrew-basic-memory
|
||||
2. Verify formula version matches release
|
||||
3. Test Homebrew installation: `brew install basicmachines-co/basic-memory/basic-memory`
|
||||
|
||||
#### MCP Registry Publication
|
||||
|
||||
After PyPI release is published, update the MCP registry:
|
||||
|
||||
1. **Verify PyPI Release**
|
||||
- Confirm package is live: https://pypi.org/project/basic-memory/<version>/
|
||||
- The `server.json` version was auto-updated by `just release`
|
||||
|
||||
2. **Publish to MCP Registry**
|
||||
```bash
|
||||
cd /Users/drew/code/basic-memory
|
||||
mcp-publisher publish
|
||||
```
|
||||
|
||||
If not authenticated:
|
||||
```bash
|
||||
mcp-publisher login github
|
||||
# Follow device authentication flow
|
||||
mcp-publisher publish
|
||||
```
|
||||
|
||||
3. **Verify Publication**
|
||||
```bash
|
||||
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=basic-memory"
|
||||
```
|
||||
|
||||
**Note:** The `mcp-publisher` CLI can be installed via Homebrew (`brew install mcp-publisher`) or from GitHub releases.
|
||||
|
||||
#### Website Updates
|
||||
|
||||
**1. basicmachines.co** (`/Users/drew/code/basicmachines.co`)
|
||||
- **Goal**: Update version number displayed on the homepage
|
||||
- **Location**: Search for "Basic Memory v0." in the codebase to find version displays
|
||||
- **What to update**:
|
||||
- Hero section heading that shows "Basic Memory v{VERSION}"
|
||||
- "What's New in v{VERSION}" section heading
|
||||
- Feature highlights array (look for array of features with title/description)
|
||||
- **Process**:
|
||||
1. Pull latest from GitHub: `git pull origin main`
|
||||
2. Create release branch: `git checkout -b release/v{VERSION}`
|
||||
3. Search codebase for current version number (e.g., "v0.16.1")
|
||||
4. Update version numbers to new release version
|
||||
5. Update feature highlights with 3-5 key features from this release (extract from CHANGELOG.md)
|
||||
6. Commit changes: `git commit -m "chore: update to v{VERSION}"`
|
||||
7. Push branch: `git push origin release/v{VERSION}`
|
||||
- **Deploy**: Follow deployment process for basicmachines.co
|
||||
|
||||
**2. docs.basicmemory.com** (`/Users/drew/code/docs.basicmemory.com`)
|
||||
- **Goal**: Add new release notes section to the latest-releases page
|
||||
- **File**: `src/pages/latest-releases.mdx`
|
||||
- **What to do**:
|
||||
1. Pull latest from GitHub: `git pull origin main`
|
||||
2. Create release branch: `git checkout -b release/v{VERSION}`
|
||||
3. Read the existing file to understand the format and structure
|
||||
4. Read `/Users/drew/code/basic-memory/CHANGELOG.md` to get release content
|
||||
5. Add new release section **at the top** (after MDX imports, before other releases)
|
||||
6. Follow the existing pattern:
|
||||
- Heading: `## [v{VERSION}](github-link) — YYYY-MM-DD`
|
||||
- Focus statement if applicable
|
||||
- `<Info>` block with highlights (3-5 key items)
|
||||
- Sections for Features, Bug Fixes, Breaking Changes, etc.
|
||||
- Link to full changelog at the end
|
||||
- Separator `---` between releases
|
||||
7. Commit changes: `git commit -m "docs: add v{VERSION} release notes"`
|
||||
8. Push branch: `git push origin release/v{VERSION}`
|
||||
- **Source content**: Extract and format sections from CHANGELOG.md for this version
|
||||
- **Deploy**: Follow deployment process for docs.basicmemory.com
|
||||
|
||||
**4. Announce Release**
|
||||
- Post to Discord community if significant changes
|
||||
- Update social media if major release
|
||||
- Notify users via appropriate channels
|
||||
|
||||
## Pre-conditions Check
|
||||
Before starting, verify:
|
||||
- [ ] All beta testing is complete
|
||||
- [ ] Critical bugs are fixed
|
||||
- [ ] Breaking changes are documented
|
||||
- [ ] CHANGELOG.md is updated (if needed)
|
||||
- [ ] Version number follows semantic versioning
|
||||
|
||||
## Error Handling
|
||||
- If `just release` fails, examine the error output for specific issues
|
||||
- If quality checks fail, fix issues and retry
|
||||
- If changelog entry missing, update CHANGELOG.md and commit before retrying
|
||||
- If GitHub Actions fail, check workflow logs for debugging
|
||||
|
||||
## Success Output
|
||||
```
|
||||
🎉 Stable Release v0.13.2 Created Successfully!
|
||||
|
||||
🏷️ Tag: v0.13.2
|
||||
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.2
|
||||
📦 PyPI: https://pypi.org/project/basic-memory/0.13.2/
|
||||
🍺 Homebrew: https://github.com/basicmachines-co/homebrew-basic-memory
|
||||
🔌 MCP Registry: https://registry.modelcontextprotocol.io
|
||||
🚀 GitHub Actions: Completed
|
||||
|
||||
Install with pip/uv:
|
||||
uv tool install basic-memory
|
||||
|
||||
Install with Homebrew:
|
||||
brew install basicmachines-co/basic-memory/basic-memory
|
||||
|
||||
Users can now upgrade:
|
||||
uv tool upgrade basic-memory
|
||||
brew upgrade basic-memory
|
||||
```
|
||||
|
||||
## Context
|
||||
- This creates production releases used by end users
|
||||
- Must pass all quality gates before proceeding
|
||||
- Uses the automated justfile target for consistency
|
||||
- Version is automatically updated in `__init__.py` and `server.json`
|
||||
- Triggers automated GitHub release with changelog
|
||||
- Package is published to PyPI for `pip` and `uv` users
|
||||
- Homebrew formula is automatically updated for stable releases
|
||||
- MCP Registry is updated manually via `mcp-publisher publish`
|
||||
- Supports multiple installation methods (uv, pip, Homebrew)
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
allowed-tools: mcp__basic-memory__write_note, mcp__basic-memory__read_note, mcp__basic-memory__search_notes, mcp__basic-memory__edit_note
|
||||
argument-hint: [create|status|show|review] [spec-name]
|
||||
description: Manage specifications in our development process
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Specifications are managed in the Basic Memory "specs" project. All specs live in a centralized location accessible across all repositories via MCP tools.
|
||||
|
||||
See SPEC-1 and SPEC-2 in the "specs" project for the full specification-driven development process.
|
||||
|
||||
Available commands:
|
||||
- `create [name]` - Create new specification
|
||||
- `status` - Show all spec statuses
|
||||
- `show [spec-name]` - Read a specific spec
|
||||
- `review [spec-name]` - Review implementation against spec
|
||||
|
||||
## Your task
|
||||
|
||||
Execute the spec command: `/spec $ARGUMENTS`
|
||||
|
||||
### If command is "create":
|
||||
1. Get next SPEC number by searching existing specs in "specs" project
|
||||
2. Create new spec using template from SPEC-2
|
||||
3. Use mcp__basic-memory__write_note with project="specs"
|
||||
4. Include standard sections: Why, What, How, How to Evaluate
|
||||
|
||||
### If command is "status":
|
||||
1. Use mcp__basic-memory__search_notes with project="specs"
|
||||
2. Display table with spec number, title, and progress
|
||||
3. Show completion status from checkboxes in content
|
||||
|
||||
### If command is "show":
|
||||
1. Use mcp__basic-memory__read_note with project="specs"
|
||||
2. Display the full spec content
|
||||
|
||||
### If command is "review":
|
||||
1. Read the specified spec and its "How to Evaluate" section
|
||||
2. Review current implementation against success criteria with careful evaluation of:
|
||||
- **Functional completeness** - All specified features working
|
||||
- **Test coverage analysis** - Actual test files and coverage percentage
|
||||
- Count existing test files vs required components/APIs/composables
|
||||
- Verify unit tests, integration tests, and end-to-end tests
|
||||
- Check for missing test categories (component, API, workflow)
|
||||
- **Code quality metrics** - TypeScript compilation, linting, performance
|
||||
- **Architecture compliance** - Component isolation, state management patterns
|
||||
- **Documentation completeness** - Implementation matches specification
|
||||
3. Provide honest, accurate assessment - do not overstate completeness
|
||||
4. Document findings and update spec with review results using mcp__basic-memory__edit_note
|
||||
5. If gaps found, clearly identify what still needs to be implemented/tested
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"enabledPlugins": {}
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,28 @@
|
||||
# Basic Memory Environment Variables Example
|
||||
# Copy this file to .env and customize as needed
|
||||
# Note: .env files are gitignored and should never be committed
|
||||
|
||||
# ============================================================================
|
||||
# PostgreSQL Test Database Configuration
|
||||
# ============================================================================
|
||||
# These variables allow you to override the default test database credentials
|
||||
# Default values match docker-compose-postgres.yml for local development
|
||||
#
|
||||
# Only needed if you want to use different credentials or a remote test database
|
||||
# By default, tests use: postgresql://basic_memory_user:dev_password@localhost:5433/basic_memory_test
|
||||
|
||||
# Full PostgreSQL test database URL (used by tests and migrations)
|
||||
# POSTGRES_TEST_URL=postgresql+asyncpg://basic_memory_user:dev_password@localhost:5433/basic_memory_test
|
||||
|
||||
# Individual components (used by justfile postgres-reset command)
|
||||
# POSTGRES_USER=basic_memory_user
|
||||
# POSTGRES_TEST_DB=basic_memory_test
|
||||
|
||||
# ============================================================================
|
||||
# Production Database Configuration
|
||||
# ============================================================================
|
||||
# For production use, set these in your deployment environment
|
||||
# DO NOT use the test credentials above in production!
|
||||
|
||||
# BASIC_MEMORY_DATABASE_BACKEND=postgres # or "sqlite"
|
||||
# BASIC_MEMORY_DATABASE_URL=postgresql+asyncpg://user:password@host:port/database
|
||||
@@ -0,0 +1,12 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
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
|
||||
- [ ] **100% test coverage maintained** (use `# pragma: no cover` only for truly hard-to-test code)
|
||||
- [ ] Documentation updated (README, docstrings)
|
||||
- [ ] CLAUDE.md updated if conventions change
|
||||
|
||||
## Basic Memory Architecture
|
||||
- [ ] MCP tools follow atomic, composable design
|
||||
- [ ] Database changes include Alembic migrations
|
||||
- [ ] Preserves local-first architecture principles
|
||||
- [ ] Knowledge graph operations maintain consistency
|
||||
- [ ] Markdown file handling preserves integrity
|
||||
- [ ] AI-human collaboration patterns followed
|
||||
|
||||
## Security & Performance
|
||||
- [ ] No hardcoded secrets or credentials
|
||||
- [ ] Input validation for MCP tools
|
||||
- [ ] Proper error handling and logging
|
||||
- [ ] Performance considerations addressed
|
||||
- [ ] No sensitive data in logs or commits
|
||||
|
||||
## Compatability
|
||||
- [ ] File path comparisons must be windows compatible
|
||||
- [ ] Avoid using emojis and unicode characters in console and log output
|
||||
|
||||
Read the CLAUDE.md file for detailed project context. For each checklist item, verify if it's satisfied and comment on any that need attention. Use inline comments for specific code issues and post a summary with checklist results.
|
||||
|
||||
# Allow broader tool access for thorough code review
|
||||
claude_args: '--allowed-tools "Bash(gh pr:*),Bash(gh issue:*),Bash(gh api:*),Bash(git log:*),Bash(git show:*),Read,Grep,Glob"'
|
||||
@@ -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"'
|
||||
@@ -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:*)'
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Tests
|
||||
|
||||
concurrency:
|
||||
group: bm-ci-${{ github.workflow }}-${{ github.repository }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
@@ -7,12 +11,61 @@ on:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
static-checks:
|
||||
name: Static Checks (Python 3.12)
|
||||
timeout-minutes: 20
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: "pip"
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
pip install uv
|
||||
|
||||
- uses: extractions/setup-just@v3
|
||||
|
||||
- name: Create virtual env
|
||||
run: |
|
||||
uv venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run type checks
|
||||
run: |
|
||||
just typecheck
|
||||
|
||||
- name: Run linting
|
||||
run: |
|
||||
just lint
|
||||
|
||||
test-sqlite-unit:
|
||||
name: Test SQLite Unit (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
||||
timeout-minutes: 30
|
||||
needs: [static-checks]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ "3.12" ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.12"
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.13"
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.14"
|
||||
- os: windows-latest
|
||||
python-version: "3.12"
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -29,19 +82,186 @@ jobs:
|
||||
run: |
|
||||
pip install uv
|
||||
|
||||
- uses: extractions/setup-just@v3
|
||||
|
||||
- name: Create virtual env
|
||||
run: |
|
||||
uv venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install -e .[dev]
|
||||
|
||||
- name: Run type checks
|
||||
run: |
|
||||
uv run make type-check
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
uv pip install pytest pytest-cov
|
||||
uv run make test
|
||||
just test-unit-sqlite
|
||||
|
||||
test-sqlite-integration:
|
||||
name: Test SQLite Integration (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
||||
timeout-minutes: 45
|
||||
needs: [static-checks]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.12"
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.13"
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.14"
|
||||
- os: windows-latest
|
||||
python-version: "3.12"
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
pip install uv
|
||||
|
||||
- uses: extractions/setup-just@v3
|
||||
|
||||
- name: Create virtual env
|
||||
run: |
|
||||
uv venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
just test-int-sqlite
|
||||
|
||||
test-postgres-unit:
|
||||
name: Test Postgres Unit (Python ${{ matrix.python-version }})
|
||||
timeout-minutes: 30
|
||||
needs: [static-checks]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- python-version: "3.12"
|
||||
- python-version: "3.13"
|
||||
- python-version: "3.14"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Note: No services section needed - testcontainers handles Postgres in Docker
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
pip install uv
|
||||
|
||||
- uses: extractions/setup-just@v3
|
||||
|
||||
- name: Create virtual env
|
||||
run: |
|
||||
uv venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
just test-unit-postgres
|
||||
|
||||
test-postgres-integration:
|
||||
name: Test Postgres Integration (Python ${{ matrix.python-version }})
|
||||
timeout-minutes: 45
|
||||
needs: [static-checks]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- python-version: "3.12"
|
||||
- python-version: "3.13"
|
||||
- python-version: "3.14"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Note: No services section needed - testcontainers handles Postgres in Docker
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
pip install uv
|
||||
|
||||
- uses: extractions/setup-just@v3
|
||||
|
||||
- name: Create virtual env
|
||||
run: |
|
||||
uv venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
just test-int-postgres
|
||||
|
||||
test-semantic:
|
||||
name: Test Semantic (Python 3.12)
|
||||
timeout-minutes: 45
|
||||
needs: [static-checks]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: "pip"
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
pip install uv
|
||||
|
||||
- uses: extractions/setup-just@v3
|
||||
|
||||
- name: Create virtual env
|
||||
run: |
|
||||
uv venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
just test-semantic
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
*.py[cod]
|
||||
__pycache__/
|
||||
.pytest_cache/
|
||||
.testmondata*
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
@@ -42,8 +43,18 @@ ENV/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
/.coverage.*
|
||||
.coverage.*
|
||||
|
||||
# obsidian docs:
|
||||
/docs/.obsidian/
|
||||
/examples/.obsidian/
|
||||
/examples/.obsidian/
|
||||
/examples/.basic-memory/
|
||||
|
||||
|
||||
# claude action
|
||||
claude-output
|
||||
**/.claude/settings.local.json
|
||||
.mcp.json
|
||||
.mcpregistry_*
|
||||
/.testmondata
|
||||
.benchmarks/
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.12
|
||||
3.14
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
# AGENTS.md - Basic Memory Project Guide
|
||||
|
||||
## Project Overview
|
||||
|
||||
Basic Memory is a local-first knowledge management system built on the Model Context Protocol (MCP). It enables
|
||||
bidirectional communication between LLMs (like Claude) and markdown files, creating a personal knowledge graph that can
|
||||
be traversed using links between documents.
|
||||
|
||||
## CODEBASE DEVELOPMENT
|
||||
|
||||
### Project information
|
||||
|
||||
See the [README.md](README.md) file for a project overview.
|
||||
|
||||
### Build and Test Commands
|
||||
|
||||
- Install: `just install` or `pip install -e ".[dev]"`
|
||||
- Run all tests (SQLite + Postgres): `just test`
|
||||
- Run all tests against SQLite: `just test-sqlite`
|
||||
- Run all tests against Postgres: `just test-postgres` (uses testcontainers)
|
||||
- Run unit tests (SQLite): `just test-unit-sqlite`
|
||||
- Run unit tests (Postgres): `just test-unit-postgres`
|
||||
- Run integration tests (SQLite): `just test-int-sqlite`
|
||||
- Run integration tests (Postgres): `just test-int-postgres`
|
||||
- Run impacted tests: `just testmon` (pytest-testmon)
|
||||
- Run MCP smoke test: `just test-smoke`
|
||||
- Fast local loop: `just fast-check`
|
||||
- Local consistency check: `just doctor`
|
||||
- Generate HTML coverage: `just coverage`
|
||||
- Single test: `pytest tests/path/to/test_file.py::test_function_name`
|
||||
- Run benchmarks: `pytest test-int/test_sync_performance_benchmark.py -v -m "benchmark and not slow"`
|
||||
- Lint: `just lint` or `ruff check . --fix`
|
||||
- Type check: `just typecheck` or `uv run pyright`
|
||||
- Type check (supplemental): `just typecheck-ty` or `uv run ty check src/`
|
||||
- Format: `just format` or `uv run ruff format .`
|
||||
- Run all code checks: `just check` (runs lint, format, typecheck, test)
|
||||
- Create db migration: `just migration "Your migration message"`
|
||||
- Run development MCP Inspector: `just run-inspector`
|
||||
|
||||
**Note:** Project requires Python 3.12+ (uses type parameter syntax and `type` aliases introduced in 3.12)
|
||||
|
||||
**Postgres Testing:** Uses [testcontainers](https://testcontainers-python.readthedocs.io/) which automatically spins up a Postgres instance in Docker. No manual database setup required - just have Docker running.
|
||||
|
||||
**Doctor Note:** `just doctor` runs with a temporary HOME/config so it won't touch your local Basic Memory settings. It leaves temp dirs in `/tmp` (safe to ignore or remove).
|
||||
|
||||
**Testmon Note:** When no files have changed, `just testmon` may collect 0 tests. That's expected and means no impacted tests were detected.
|
||||
|
||||
### Code/Test/Verify Loop (fast path)
|
||||
|
||||
1) **Code:** make changes.
|
||||
2) **Test:** `just fast-check` (lint/format/typecheck + impacted tests + MCP smoke).
|
||||
3) **Verify:** `just doctor` (end-to-end file ↔ DB loop in a temp project).
|
||||
4) **Full gate (when needed):** `just test` or `just check` for SQLite + Postgres.
|
||||
|
||||
If testmon is “cold,” the first run may be long. Subsequent runs get much faster.
|
||||
|
||||
### Test Structure
|
||||
|
||||
- `tests/` - Unit tests for individual components (mocked, fast)
|
||||
- `test-int/` - Integration tests for real-world scenarios (no mocks, realistic)
|
||||
- Both directories are covered by unified coverage reporting
|
||||
- Benchmark tests in `test-int/` are marked with `@pytest.mark.benchmark`
|
||||
- Slow tests are marked with `@pytest.mark.slow`
|
||||
- Smoke tests are marked with `@pytest.mark.smoke`
|
||||
|
||||
### Code Style Guidelines
|
||||
|
||||
- Line length: 100 characters max
|
||||
- Python 3.12+ with full type annotations (uses type parameters and type aliases)
|
||||
- Format with ruff (consistent styling)
|
||||
- Import order: standard lib, third-party, local imports
|
||||
- Naming: snake_case for functions/variables, PascalCase for classes
|
||||
- Prefer async patterns with SQLAlchemy 2.0
|
||||
- Use Pydantic v2 for data validation and schemas
|
||||
- CLI uses Typer for command structure
|
||||
- API uses FastAPI for endpoints
|
||||
- Follow the repository pattern for data access
|
||||
- Tools communicate to api routers via the httpx ASGI client (in process)
|
||||
|
||||
### Code Change Guidelines
|
||||
|
||||
- **Full file read before edits**: Before editing any file, read it in full first to ensure complete context; partial reads lead to corrupted edits
|
||||
- **Minimize diffs**: Prefer the smallest change that satisfies the request. Avoid unrelated refactors or style rewrites unless necessary for correctness
|
||||
- **No speculative getattr**: Never use `getattr(obj, "attr", default)` when unsure about attribute names. Check the class definition or source code first
|
||||
- **Fail fast**: Write code with fail-fast logic by default. Do not swallow exceptions with errors or warnings
|
||||
- **No fallback logic**: Do not add fallback logic unless explicitly told to and agreed with the user
|
||||
- **No guessing**: Do not say "The issue is..." before you actually know what the issue is. Investigate first.
|
||||
|
||||
### Literate Programming Style
|
||||
|
||||
Code should tell a story. Comments must explain the "why" and narrative flow, not just the "what".
|
||||
|
||||
**Section Headers:**
|
||||
For files with multiple phases of logic, add section headers so the control flow reads like chapters:
|
||||
```python
|
||||
# --- Authentication ---
|
||||
# ... auth logic ...
|
||||
|
||||
# --- Data Validation ---
|
||||
# ... validation logic ...
|
||||
|
||||
# --- Business Logic ---
|
||||
# ... core logic ...
|
||||
```
|
||||
|
||||
**Decision Point Comments:**
|
||||
For conditionals that materially change behavior (gates, fallbacks, retries, feature flags), add comments with:
|
||||
- **Trigger**: what condition causes this branch
|
||||
- **Why**: the rationale (cost, correctness, UX, determinism)
|
||||
- **Outcome**: what changes downstream
|
||||
|
||||
```python
|
||||
# Trigger: project has no active sync watcher
|
||||
# Why: avoid duplicate file system watchers consuming resources
|
||||
# Outcome: starts new watcher, registers in active_watchers dict
|
||||
if project_id not in active_watchers:
|
||||
start_watcher(project_id)
|
||||
```
|
||||
|
||||
**Constraint Comments:**
|
||||
If code exists because of a constraint (async requirements, rate limits, schema compatibility), explain the constraint near the code:
|
||||
```python
|
||||
# SQLite requires WAL mode for concurrent read/write access
|
||||
connection.execute("PRAGMA journal_mode=WAL")
|
||||
```
|
||||
|
||||
**What NOT to Comment:**
|
||||
Avoid comments that restate obvious code:
|
||||
```python
|
||||
# Bad - restates code
|
||||
counter += 1 # increment counter
|
||||
|
||||
# Good - explains why
|
||||
counter += 1 # track retries for backoff calculation
|
||||
```
|
||||
|
||||
### Codebase Architecture
|
||||
|
||||
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed architecture documentation.
|
||||
|
||||
**Directory Structure:**
|
||||
- `/alembic` - Alembic db migrations
|
||||
- `/api` - FastAPI REST endpoints + `container.py` composition root
|
||||
- `/cli` - Typer CLI + `container.py` composition root
|
||||
- `/deps` - Feature-scoped FastAPI dependencies (config, db, projects, repositories, services, importers)
|
||||
- `/importers` - Import functionality for Claude, ChatGPT, and other sources
|
||||
- `/markdown` - Markdown parsing and processing
|
||||
- `/mcp` - MCP server + `container.py` composition root + `clients/` typed API clients
|
||||
- `/models` - SQLAlchemy ORM models
|
||||
- `/repository` - Data access layer
|
||||
- `/schemas` - Pydantic models for validation
|
||||
- `/services` - Business logic layer
|
||||
- `/sync` - File synchronization services + `coordinator.py` for lifecycle management
|
||||
|
||||
**Composition Roots:**
|
||||
Each entrypoint (API, MCP, CLI) has a composition root that:
|
||||
- Reads `ConfigManager` (the only place that reads global config)
|
||||
- Resolves runtime mode via `RuntimeMode` enum (TEST > CLOUD > LOCAL)
|
||||
- Provides dependencies to downstream code explicitly
|
||||
|
||||
**Typed API Clients (MCP):**
|
||||
MCP tools use typed clients in `mcp/clients/` to communicate with the API:
|
||||
- `KnowledgeClient` - Entity CRUD operations
|
||||
- `SearchClient` - Search operations
|
||||
- `MemoryClient` - Context building
|
||||
- `DirectoryClient` - Directory listing
|
||||
- `ResourceClient` - Resource reading
|
||||
- `ProjectClient` - Project management
|
||||
|
||||
Flow: MCP Tool → Typed Client → HTTP API → Router → Service → Repository
|
||||
|
||||
### Development Notes
|
||||
|
||||
- MCP tools are defined in src/basic_memory/mcp/tools/
|
||||
- MCP prompts are defined in src/basic_memory/mcp/prompts/
|
||||
- MCP tools should be atomic, composable operations
|
||||
- Use `textwrap.dedent()` for multi-line string formatting in prompts and tools
|
||||
- MCP Prompts are used to invoke tools and format content with instructions for an LLM
|
||||
- Schema changes require Alembic migrations
|
||||
- SQLite is used for indexing and full text search, files are source of truth
|
||||
- Testing uses pytest with asyncio support (strict mode)
|
||||
- Unit tests (`tests/`) use mocks when necessary; integration tests (`test-int/`) use real implementations
|
||||
- By default, tests run against SQLite (fast, no Docker needed)
|
||||
- Set `BASIC_MEMORY_TEST_POSTGRES=1` to run against Postgres (uses testcontainers - Docker required)
|
||||
- Each test runs in a standalone environment with isolated database and tmp_path directory
|
||||
- CI runs SQLite and Postgres tests in parallel for faster feedback
|
||||
- Performance benchmarks are in `test-int/test_sync_performance_benchmark.py`
|
||||
- Use pytest markers: `@pytest.mark.benchmark` for benchmarks, `@pytest.mark.slow` for slow tests
|
||||
- **Coverage must stay at 100%**: Write tests for new code. Only use `# pragma: no cover` when tests would require excessive mocking (e.g., TYPE_CHECKING blocks, error handlers that need failure injection, runtime-mode-dependent code paths)
|
||||
|
||||
### Async Client Pattern (Important!)
|
||||
|
||||
**MCP tools use `get_project_client()` for per-project routing:**
|
||||
|
||||
```python
|
||||
from basic_memory.mcp.project_context import get_project_client
|
||||
|
||||
@mcp.tool()
|
||||
async def my_tool(project: str | None = None, context: Context | None = None):
|
||||
async with get_project_client(project, context) as (client, active_project):
|
||||
# client is routed based on project's mode (local ASGI or cloud HTTP)
|
||||
response = await call_get(client, "/path")
|
||||
return response
|
||||
```
|
||||
|
||||
**CLI commands and non-project-scoped code use `get_client()` directly:**
|
||||
|
||||
```python
|
||||
from basic_memory.mcp.async_client import get_client
|
||||
|
||||
async def my_cli_command():
|
||||
async with get_client() as client:
|
||||
response = await call_get(client, "/path")
|
||||
return response
|
||||
|
||||
# Per-project routing (when project name is known):
|
||||
async with get_client(project_name="research") as client:
|
||||
...
|
||||
```
|
||||
|
||||
**Do NOT use:**
|
||||
- ❌ `from basic_memory.mcp.async_client import client` (deprecated module-level client)
|
||||
- ❌ Manual auth header management
|
||||
- ❌ `inject_auth_header()` (deleted)
|
||||
- ❌ Separate `get_client()` + `get_active_project()` in MCP tools (use `get_project_client()` instead)
|
||||
|
||||
**Key principles:**
|
||||
- Auth happens at client creation, not per-request
|
||||
- Proper resource management via context managers
|
||||
- Per-project routing: each project can be LOCAL or CLOUD independently
|
||||
- Cloud projects use API key (`cloud_api_key` in config) as Bearer token
|
||||
- Routing priority: factory injection > force-local > per-project cloud > global cloud > local ASGI
|
||||
- Factory pattern enables dependency injection for cloud consolidation
|
||||
|
||||
**For cloud app integration:**
|
||||
```python
|
||||
from basic_memory.mcp import async_client
|
||||
|
||||
# Set custom factory before importing tools
|
||||
async_client.set_client_factory(your_custom_factory)
|
||||
```
|
||||
|
||||
See SPEC-16 for full context manager refactor details.
|
||||
|
||||
## BASIC MEMORY PRODUCT USAGE
|
||||
|
||||
### Knowledge Structure
|
||||
|
||||
- Entity: Any concept, document, or idea represented as a markdown file
|
||||
- Observation: A categorized fact about an entity (`- [category] content`)
|
||||
- Relation: A directional link between entities (`- relation_type [[Target]]`)
|
||||
- Frontmatter: YAML metadata at the top of markdown files
|
||||
- Knowledge representation follows precise markdown format:
|
||||
- Observations with [category] prefixes
|
||||
- Relations with WikiLinks [[Entity]]
|
||||
- Frontmatter with metadata
|
||||
|
||||
### Basic Memory Commands
|
||||
|
||||
**Local Commands:**
|
||||
- Check sync status: `basic-memory status`
|
||||
- Doctor check (file <-> DB loop): `basic-memory doctor`
|
||||
- Import from Claude: `basic-memory import claude conversations`
|
||||
- Import from ChatGPT: `basic-memory import chatgpt`
|
||||
- Import from Memory JSON: `basic-memory import memory-json`
|
||||
- Tool access: `basic-memory tool` (provides CLI access to MCP tools)
|
||||
- Continue: `basic-memory tool continue-conversation --topic="search"`
|
||||
|
||||
**Project Management:**
|
||||
- List projects: `basic-memory project list`
|
||||
- Add project: `basic-memory project add "name" ~/path`
|
||||
- Project info: `basic-memory project info`
|
||||
- Set cloud mode: `basic-memory project set-cloud "name"`
|
||||
- Set local mode: `basic-memory project set-local "name"`
|
||||
- One-way sync (local -> cloud): `basic-memory project sync`
|
||||
- Bidirectional sync: `basic-memory project bisync`
|
||||
- Integrity check: `basic-memory project check`
|
||||
|
||||
**Cloud Commands (requires subscription):**
|
||||
- Authenticate (global): `basic-memory cloud login`
|
||||
- Logout (global): `basic-memory cloud logout`
|
||||
- Check cloud status: `basic-memory cloud status`
|
||||
- Setup cloud sync: `basic-memory cloud setup`
|
||||
- Save API key: `basic-memory cloud set-key bmc_...`
|
||||
- Create API key: `basic-memory cloud create-key "name"`
|
||||
- Manage snapshots: `basic-memory cloud snapshot [create|list|delete|show|browse]`
|
||||
- Restore from snapshot: `basic-memory cloud restore <path> --snapshot <id>`
|
||||
|
||||
### MCP Capabilities
|
||||
|
||||
- Basic Memory exposes these MCP tools to LLMs:
|
||||
|
||||
**Content Management:**
|
||||
- `write_note(title, content, directory, tags)` - Create/update markdown notes with semantic observations and relations
|
||||
- `read_note(identifier, page, page_size)` - Read notes by title, permalink, or memory:// URL with knowledge graph awareness
|
||||
- `read_content(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
|
||||
- `view_note(identifier, page, page_size)` - View notes as formatted artifacts for better readability
|
||||
- `edit_note(identifier, operation, content)` - Edit notes incrementally (append, prepend, find/replace, replace_section)
|
||||
- `move_note(identifier, destination_path, is_directory)` - Move notes or directories to new locations, updating database and maintaining links
|
||||
- `delete_note(identifier, is_directory)` - Delete notes or directories from the knowledge base
|
||||
|
||||
**Knowledge Graph Navigation:**
|
||||
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation continuity
|
||||
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "1d", "1 week")
|
||||
- `list_directory(dir_name, depth, file_name_glob)` - Browse directory contents with filtering and depth control
|
||||
|
||||
**Search & Discovery:**
|
||||
- `search_notes(query, page, page_size, search_type, types, entity_types, after_date)` - Full-text search across all content with advanced filtering options
|
||||
|
||||
**Project Management:**
|
||||
- `list_memory_projects()` - List all available projects with their status
|
||||
- `create_memory_project(project_name, project_path, set_default)` - Create new Basic Memory projects
|
||||
- `delete_project(project_name)` - Delete a project from configuration
|
||||
|
||||
**Visualization:**
|
||||
- `canvas(nodes, edges, title, directory)` - Generate Obsidian canvas files for knowledge graph visualization
|
||||
|
||||
**ChatGPT-Compatible Tools:**
|
||||
- `search(query)` - Search across knowledge base (OpenAI actions compatible)
|
||||
- `fetch(id)` - Fetch full content of a search result document
|
||||
|
||||
- MCP Prompts for better AI interaction:
|
||||
- `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
|
||||
- `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
|
||||
- `search(query, after_date)` - Search with detailed, formatted results for better context understanding
|
||||
- `recent_activity(timeframe)` - View recently changed items with formatted output
|
||||
|
||||
### Cloud Features (v0.15.0+)
|
||||
|
||||
Basic Memory now supports cloud synchronization and storage (requires active subscription):
|
||||
|
||||
**Authentication:**
|
||||
- JWT-based authentication with subscription validation
|
||||
- Secure session management with token refresh
|
||||
- Support for multiple cloud projects
|
||||
|
||||
**Bidirectional Sync:**
|
||||
- rclone bisync integration for two-way synchronization
|
||||
- Conflict resolution and integrity verification
|
||||
- Real-time sync with change detection
|
||||
- Mount/unmount cloud storage for direct file access
|
||||
|
||||
**Cloud Project Management:**
|
||||
- Create and manage projects in the cloud
|
||||
- Toggle between local and cloud modes
|
||||
- Per-project sync configuration
|
||||
- Subscription-based access control
|
||||
|
||||
**Security & Performance:**
|
||||
- Removed .env file loading for improved security
|
||||
- .gitignore integration (respects gitignored files)
|
||||
- WAL mode for SQLite performance
|
||||
- Background relation resolution (non-blocking startup)
|
||||
- API performance optimizations (SPEC-11)
|
||||
|
||||
**Per-Project Cloud Routing:**
|
||||
|
||||
Individual projects can be routed through the cloud while others stay local, using an API key:
|
||||
|
||||
```bash
|
||||
# Save API key and set project to cloud mode
|
||||
basic-memory cloud set-key bmc_abc123...
|
||||
basic-memory project set-cloud research # route through cloud
|
||||
basic-memory project set-local research # revert to local
|
||||
```
|
||||
|
||||
MCP tools use `get_project_client()` which automatically routes based on the project's mode. Cloud projects use the `cloud_api_key` from config as Bearer token.
|
||||
|
||||
**CLI Routing Flags (Global Cloud Mode):**
|
||||
|
||||
When global cloud mode is enabled, CLI commands route to the cloud API by default. Use `--local` and `--cloud` flags to override:
|
||||
|
||||
```bash
|
||||
# Force local routing (ignore cloud mode)
|
||||
basic-memory status --local
|
||||
basic-memory project list --local
|
||||
|
||||
# Force cloud routing (when cloud mode is disabled)
|
||||
basic-memory status --cloud
|
||||
basic-memory project info my-project --cloud
|
||||
```
|
||||
|
||||
Key behaviors:
|
||||
- The local MCP server (`basic-memory mcp`) automatically uses local routing
|
||||
- This allows simultaneous use of local Claude Desktop and cloud-based clients
|
||||
- Some commands (like `project default`, `project sync-config`, `project move`) require `--local` in cloud mode since they modify local configuration
|
||||
- Environment variable `BASIC_MEMORY_FORCE_LOCAL=true` forces local routing globally
|
||||
- Per-project cloud routing via API key works independently of global cloud mode
|
||||
|
||||
## AI-Human Collaborative Development
|
||||
|
||||
Basic Memory emerged from and enables a new kind of development process that combines human and AI capabilities. Instead
|
||||
of using AI just for code generation, we've developed a true collaborative workflow:
|
||||
|
||||
1. AI (LLM) writes initial implementation based on specifications and context
|
||||
2. Human reviews, runs tests, and commits code with any necessary adjustments
|
||||
3. Knowledge persists across conversations using Basic Memory's knowledge graph
|
||||
4. Development continues seamlessly across different AI sessions with consistent context
|
||||
5. Results improve through iterative collaboration and shared understanding
|
||||
|
||||
This approach has allowed us to tackle more complex challenges and build a more robust system than either humans or AI
|
||||
could achieve independently.
|
||||
|
||||
**Problem-Solving Guidance:**
|
||||
- If a solution isn't working after reasonable effort, suggest alternative approaches
|
||||
- Don't persist with a problematic library or pattern when better alternatives exist
|
||||
- Example: When py-pglite caused cascading test failures, switching to testcontainers-postgres was the right call
|
||||
|
||||
## GitHub Integration
|
||||
|
||||
Basic Memory has taken AI-Human collaboration to the next level by integrating Claude directly into the development workflow through GitHub:
|
||||
|
||||
### GitHub MCP Tools
|
||||
|
||||
Using the GitHub Model Context Protocol server, Claude can now:
|
||||
|
||||
- **Repository Management**:
|
||||
- View repository files and structure
|
||||
- Read file contents
|
||||
- Create new branches
|
||||
- Create and update files
|
||||
|
||||
- **Issue Management**:
|
||||
- Create new issues
|
||||
- Comment on existing issues
|
||||
- Close and update issues
|
||||
- Search across issues
|
||||
|
||||
- **Pull Request Workflow**:
|
||||
- Create pull requests
|
||||
- Review code changes
|
||||
- Add comments to PRs
|
||||
|
||||
This integration enables Claude to participate as a full team member in the development process, not just as a code generation tool. Claude's GitHub account ([bm-claudeai](https://github.com/bm-claudeai)) is a member of the Basic Machines organization with direct contributor access to the codebase.
|
||||
|
||||
### Collaborative Development Process
|
||||
|
||||
With GitHub integration, the development workflow includes:
|
||||
|
||||
1. **Direct code review** - Claude can analyze PRs and provide detailed feedback
|
||||
2. **Contribution tracking** - All of Claude's contributions are properly attributed in the Git history
|
||||
3. **Branch management** - Claude can create feature branches for implementations
|
||||
4. **Documentation maintenance** - Claude can keep documentation updated as the code evolves
|
||||
5. **Code Commits**: ALWAYS sign off commits with `git commit -s`
|
||||
|
||||
This level of integration represents a new paradigm in AI-human collaboration, where the AI assistant becomes a full-fledged team member rather than just a tool for generating code snippets.
|
||||
@@ -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,133 +0,0 @@
|
||||
# CLAUDE.md - Basic Memory Project Guide
|
||||
|
||||
## Project Overview
|
||||
|
||||
Basic Memory is a local-first knowledge management system built on the Model Context Protocol (MCP). It enables
|
||||
bidirectional communication between LLMs (like Claude) and markdown files, creating a personal knowledge graph that can
|
||||
be traversed using links between documents.
|
||||
|
||||
## CODEBASE DEVELOPMENT
|
||||
|
||||
### Project information
|
||||
|
||||
See the [README.md](README.md) file for a project overview.
|
||||
|
||||
### Build and Test Commands
|
||||
|
||||
- Install: `make install` or `pip install -e ".[dev]"`
|
||||
- Run tests: `uv run pytest -p pytest_mock -v` or `make test`
|
||||
- Single test: `pytest tests/path/to/test_file.py::test_function_name`
|
||||
- Lint: `make lint` or `ruff check . --fix`
|
||||
- Type check: `make type-check` or `uv run pyright`
|
||||
- Format: `make format` or `uv run ruff format .`
|
||||
- Run all code checks: `make check` (runs lint, format, type-check, test)
|
||||
- Create db migration: `make migration m="Your migration message"`
|
||||
- Run development MCP Inspector: `make run-inspector`
|
||||
|
||||
### Code Style Guidelines
|
||||
|
||||
- Line length: 100 characters max
|
||||
- Python 3.12+ with full type annotations
|
||||
- Format with ruff (consistent styling)
|
||||
- Import order: standard lib, third-party, local imports
|
||||
- Naming: snake_case for functions/variables, PascalCase for classes
|
||||
- Prefer async patterns with SQLAlchemy 2.0
|
||||
- Use Pydantic v2 for data validation and schemas
|
||||
- CLI uses Typer for command structure
|
||||
- API uses FastAPI for endpoints
|
||||
- Follow the repository pattern for data access
|
||||
- Tools communicate to api routers via the httpx ASGI client (in process)
|
||||
|
||||
### Codebase Architecture
|
||||
|
||||
- `/alembic` - Alembic db migrations
|
||||
- `/api` - FastAPI implementation of REST endpoints
|
||||
- `/cli` - Typer command-line interface
|
||||
- `/markdown` - Markdown parsing and processing
|
||||
- `/mcp` - Model Context Protocol server implementation
|
||||
- `/models` - SQLAlchemy ORM models
|
||||
- `/repository` - Data access layer
|
||||
- `/schemas` - Pydantic models for validation
|
||||
- `/services` - Business logic layer
|
||||
- `/sync` - File synchronization services
|
||||
|
||||
### Development Notes
|
||||
|
||||
- MCP tools are defined in src/basic_memory/mcp/tools/
|
||||
- MCP prompts are defined in src/basic_memory/mcp/prompts/
|
||||
- MCP tools should be atomic, composable operations
|
||||
- Use `textwrap.dedent()` for multi-line string formatting in prompts and tools
|
||||
- MCP Prompts are used to invoke tools and format content with instructions for an LLM
|
||||
- Schema changes require Alembic migrations
|
||||
- SQLite is used for indexing and full text search, files are source of truth
|
||||
- Testing uses pytest with asyncio support (strict mode)
|
||||
- Test database uses in-memory SQLite
|
||||
- Avoid creating mocks in tests in most circumstances.
|
||||
- Each test runs in a standalone enviroment with in memory SQLite and tmp_file directory
|
||||
|
||||
## BASIC MEMORY PRODUCT USAGE
|
||||
|
||||
### Knowledge Structure
|
||||
|
||||
- Entity: Any concept, document, or idea represented as a markdown file
|
||||
- Observation: A categorized fact about an entity (`- [category] content`)
|
||||
- Relation: A directional link between entities (`- relation_type [[Target]]`)
|
||||
- Frontmatter: YAML metadata at the top of markdown files
|
||||
- Knowledge representation follows precise markdown format:
|
||||
- Observations with [category] prefixes
|
||||
- Relations with WikiLinks [[Entity]]
|
||||
- Frontmatter with metadata
|
||||
|
||||
### Basic Memory Commands
|
||||
|
||||
- Sync knowledge: `basic-memory sync` or `basic-memory sync --watch`
|
||||
- Import from Claude: `basic-memory import claude conversations`
|
||||
- Import from ChatGPT: `basic-memory import chatgpt`
|
||||
- Import from Memory JSON: `basic-memory import memory-json`
|
||||
- Check sync status: `basic-memory status`
|
||||
- Tool access: `basic-memory tools` (provides CLI access to MCP tools)
|
||||
- Guide: `basic-memory tools basic-memory-guide`
|
||||
- Continue: `basic-memory tools continue-conversation --topic="search"`
|
||||
|
||||
### MCP Capabilities
|
||||
|
||||
- Basic Memory exposes these MCP tools to LLMs:
|
||||
|
||||
**Content Management:**
|
||||
- `write_note(title, content, folder, tags)` - Create/update markdown notes with semantic observations and relations
|
||||
- `read_note(identifier, page, page_size)` - Read notes by title, permalink, or memory:// URL with knowledge graph
|
||||
awareness
|
||||
- `read_file(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
|
||||
|
||||
**Knowledge Graph Navigation:**
|
||||
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation
|
||||
continuity
|
||||
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "
|
||||
1d", "1 week")
|
||||
|
||||
**Search & Discovery:**
|
||||
- `search(query, page, page_size)` - Full-text search across all content with filtering options
|
||||
|
||||
**Visualization:**
|
||||
- `canvas(nodes, edges, title, folder)` - Generate Obsidian canvas files for knowledge graph visualization
|
||||
|
||||
- MCP Prompts for better AI interaction:
|
||||
- `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
|
||||
- `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
|
||||
- `search(query, after_date)` - Search with detailed, formatted results for better context understanding
|
||||
- `recent_activity(timeframe)` - View recently changed items with formatted output
|
||||
- `json_canvas_spec()` - Full JSON Canvas specification for Obsidian visualization
|
||||
|
||||
## AI-Human Collaborative Development
|
||||
|
||||
Basic Memory emerged from and enables a new kind of development process that combines human and AI capabilities. Instead
|
||||
of using AI just for code generation, we've developed a true collaborative workflow:
|
||||
|
||||
1. AI (LLM) writes initial implementation based on specifications and context
|
||||
2. Human reviews, runs tests, and commits code with any necessary adjustments
|
||||
3. Knowledge persists across conversations using Basic Memory's knowledge graph
|
||||
4. Development continues seamlessly across different AI sessions with consistent context
|
||||
5. Results improve through iterative collaboration and shared understanding
|
||||
|
||||
This approach has allowed us to tackle more complex challenges and build a more robust system than either humans or AI
|
||||
could achieve independently.
|
||||
@@ -1,6 +1,7 @@
|
||||
# Contributing to Basic Memory
|
||||
|
||||
Thank you for considering contributing to Basic Memory! This document outlines the process for contributing to the project and how to get started as a developer.
|
||||
Thank you for considering contributing to Basic Memory! This document outlines the process for contributing to the
|
||||
project and how to get started as a developer.
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -14,8 +15,8 @@ Thank you for considering contributing to Basic Memory! This document outlines t
|
||||
|
||||
2. **Install Dependencies**:
|
||||
```bash
|
||||
# Using make (recommended)
|
||||
make install
|
||||
# Using just (recommended)
|
||||
just install
|
||||
|
||||
# Or using uv
|
||||
uv install -e ".[dev]"
|
||||
@@ -24,13 +25,27 @@ Thank you for considering contributing to Basic Memory! This document outlines t
|
||||
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
|
||||
# Run all tests
|
||||
make test
|
||||
# or
|
||||
uv run pytest -p pytest_mock -v
|
||||
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
4. **Run the Tests**:
|
||||
```bash
|
||||
# Run all tests with unified coverage (unit + integration)
|
||||
just test
|
||||
|
||||
# Run unit tests only (fast, no coverage)
|
||||
just test-unit
|
||||
|
||||
# Run integration tests only (fast, no coverage)
|
||||
just test-int
|
||||
|
||||
# Generate HTML coverage report
|
||||
just coverage
|
||||
|
||||
# Run a specific test
|
||||
pytest tests/path/to/test_file.py::test_function_name
|
||||
```
|
||||
@@ -48,16 +63,16 @@ Thank you for considering contributing to Basic Memory! This document outlines t
|
||||
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.
|
||||
|
||||
@@ -65,65 +80,68 @@ Thank you for considering contributing to Basic Memory! This document outlines t
|
||||
|
||||
This project is designed for collaborative development between humans and LLMs (Large Language Models):
|
||||
|
||||
1. **CLAUDE.md**: The repository includes a `CLAUDE.md` file that serves as a project guide for both humans and LLMs. This file contains:
|
||||
- Key project information and architectural overview
|
||||
- Development commands and workflows
|
||||
- Code style guidelines
|
||||
- Documentation standards
|
||||
1. **CLAUDE.md**: The repository includes a `CLAUDE.md` file that serves as a project guide for both humans and LLMs.
|
||||
This file contains:
|
||||
- Key project information and architectural overview
|
||||
- Development commands and workflows
|
||||
- Code style guidelines
|
||||
- Documentation standards
|
||||
|
||||
2. **AI-Human Collaborative Workflow**:
|
||||
- We encourage using LLMs like Claude for code generation, reviews, and documentation
|
||||
- When possible, save context in markdown files that can be referenced later
|
||||
- This enables seamless knowledge transfer between different development sessions
|
||||
- Claude can help with implementation details while you focus on architecture and design
|
||||
- We encourage using LLMs like Claude for code generation, reviews, and documentation
|
||||
- When possible, save context in markdown files that can be referenced later
|
||||
- This enables seamless knowledge transfer between different development sessions
|
||||
- Claude can help with implementation details while you focus on architecture and design
|
||||
|
||||
3. **Adding to CLAUDE.md**:
|
||||
- If you discover useful project information or common commands, consider adding them to CLAUDE.md
|
||||
- This helps all contributors (human and AI) maintain consistent knowledge of the project
|
||||
- If you discover useful project information or common commands, consider adding them to CLAUDE.md
|
||||
- This helps all contributors (human and AI) maintain consistent knowledge of the project
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. **Create a Pull Request**: Open a PR against the `main` branch with a clear title and description.
|
||||
2. **Sign the Developer Certificate of Origin (DCO)**: All contributions require signing our DCO, which certifies that you have the right to submit your contributions. This will be automatically checked by our CLA assistant when you create a PR.
|
||||
2. **Sign the Developer Certificate of Origin (DCO)**: All contributions require signing our DCO, which certifies that
|
||||
you have the right to submit your contributions. This will be automatically checked by our CLA assistant when you
|
||||
create a PR.
|
||||
3. **PR Description**: Include:
|
||||
- What the PR changes
|
||||
- Why the change is needed
|
||||
- How you tested the changes
|
||||
- Any related issues (use "Fixes #123" to automatically close issues)
|
||||
- What the PR changes
|
||||
- Why the change is needed
|
||||
- How you tested the changes
|
||||
- Any related issues (use "Fixes #123" to automatically close issues)
|
||||
4. **Code Review**: Wait for code review and address any feedback.
|
||||
5. **CI Checks**: Ensure all CI checks pass.
|
||||
6. **Merge**: Once approved, a maintainer will merge your PR.
|
||||
|
||||
## Developer Certificate of Origin
|
||||
|
||||
By contributing to this project, you agree to the [Developer Certificate of Origin (DCO)](CLA.md). This means you certify that:
|
||||
By contributing to this project, you agree to the [Developer Certificate of Origin (DCO)](CLA.md). This means you
|
||||
certify that:
|
||||
|
||||
- You have the right to submit your contributions
|
||||
- You're not knowingly submitting code with patent or copyright issues
|
||||
- Your contributions are provided under the project's license (AGPL-3.0)
|
||||
|
||||
This is a lightweight alternative to a Contributor License Agreement and helps ensure that all contributions can be properly incorporated into the project and potentially used in commercial applications.
|
||||
This is a lightweight alternative to a Contributor License Agreement and helps ensure that all contributions can be
|
||||
properly incorporated into the project and potentially used in commercial applications.
|
||||
|
||||
### Signing Your Commits
|
||||
|
||||
You can sign your commits in one of two ways:
|
||||
Sign your commit:
|
||||
|
||||
1. **Using the `-s` or `--signoff` flag**:
|
||||
```bash
|
||||
git commit -s -m "Your commit message"
|
||||
```
|
||||
This adds a `Signed-off-by` line to your commit message, certifying that you adhere to the DCO.
|
||||
**Using the `-s` or `--signoff` flag**:
|
||||
|
||||
2. **Configuring Git to automatically sign off**:
|
||||
```bash
|
||||
git config --global alias.cs 'commit -s'
|
||||
```
|
||||
Then use `git cs -m "Your commit message"` to commit with sign-off.
|
||||
```bash
|
||||
git commit -s -m "Your commit message"
|
||||
```
|
||||
|
||||
The sign-off certifies that you have the right to submit your contribution under the project's license and verifies your agreement to the DCO.
|
||||
This adds a `Signed-off-by` line to your commit message, certifying that you adhere to the DCO.
|
||||
|
||||
The sign-off certifies that you have the right to submit your contribution under the project's license and verifies your
|
||||
agreement to the DCO.
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
- **Python Version**: Python 3.12+ with full type annotations
|
||||
- **Python Version**: Python 3.12+ with full type annotations (3.12+ required for type parameter syntax)
|
||||
- **Line Length**: 100 characters maximum
|
||||
- **Formatting**: Use ruff for consistent styling
|
||||
- **Import Order**: Standard lib, third-party, local imports
|
||||
@@ -133,12 +151,112 @@ The sign-off certifies that you have the right to submit your contribution under
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
- **Coverage Target**: We aim for 100% test coverage for all code
|
||||
### Test Structure
|
||||
|
||||
Basic Memory uses two test directories with unified coverage reporting:
|
||||
|
||||
- **`tests/`**: Unit tests that test individual components in isolation
|
||||
- Fast execution with extensive mocking
|
||||
- Test individual functions, classes, and modules
|
||||
- Run with: `just test-unit` (no coverage, fast)
|
||||
|
||||
- **`test-int/`**: Integration tests that test real-world scenarios
|
||||
- Test full workflows with real database and file operations
|
||||
- Include performance benchmarks
|
||||
- More realistic but slower than unit tests
|
||||
- Run with: `just test-int` (no coverage, fast)
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests with unified coverage report
|
||||
just test
|
||||
|
||||
# Run only unit tests (fast iteration)
|
||||
just test-unit
|
||||
|
||||
# Run only integration tests
|
||||
just test-int
|
||||
|
||||
# Generate HTML coverage report
|
||||
just coverage
|
||||
|
||||
# Run specific test
|
||||
pytest tests/path/to/test_file.py::test_function_name
|
||||
|
||||
# Run tests excluding benchmarks
|
||||
pytest -m "not benchmark"
|
||||
|
||||
# Run only benchmark tests
|
||||
pytest -m benchmark test-int/test_sync_performance_benchmark.py
|
||||
```
|
||||
|
||||
### Performance Benchmarks
|
||||
|
||||
The `test-int/test_sync_performance_benchmark.py` file contains performance benchmarks that measure sync and indexing speed:
|
||||
|
||||
- `test_benchmark_sync_100_files` - Small repository performance
|
||||
- `test_benchmark_sync_500_files` - Medium repository performance
|
||||
- `test_benchmark_sync_1000_files` - Large repository performance (marked slow)
|
||||
- `test_benchmark_resync_no_changes` - Re-sync performance baseline
|
||||
|
||||
Run benchmarks with:
|
||||
```bash
|
||||
# Run all benchmarks (excluding slow ones)
|
||||
pytest test-int/test_sync_performance_benchmark.py -v -m "benchmark and not slow"
|
||||
|
||||
# Run all benchmarks including slow ones
|
||||
pytest test-int/test_sync_performance_benchmark.py -v -m benchmark
|
||||
|
||||
# Run specific benchmark
|
||||
pytest test-int/test_sync_performance_benchmark.py::test_benchmark_sync_100_files -v
|
||||
```
|
||||
|
||||
See `test-int/BENCHMARKS.md` for detailed benchmark documentation.
|
||||
|
||||
### Testing Best Practices
|
||||
|
||||
- **Coverage Target**: We aim for high test coverage for all code
|
||||
- **Test Framework**: Use pytest for unit and integration tests
|
||||
- **Mocking**: Use pytest-mock for mocking dependencies
|
||||
- **Mocking**: Avoid mocking in integration tests; use sparingly in unit tests
|
||||
- **Edge Cases**: Test both normal operation and edge cases
|
||||
- **Database Testing**: Use in-memory SQLite for testing database operations
|
||||
- **Fixtures**: Use pytest fixtures for setup and teardown
|
||||
- **Fixtures**: Use async pytest fixtures for setup and teardown
|
||||
- **Markers**: Use `@pytest.mark.benchmark` for benchmarks, `@pytest.mark.slow` for slow tests
|
||||
|
||||
## 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
|
||||
|
||||
@@ -156,4 +274,4 @@ All contributors must follow the [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
## Thank You!
|
||||
|
||||
Your contributions help make Basic Memory better for everyone. We appreciate your time and effort!
|
||||
Your contributions help make Basic Memory better. We appreciate your time and effort!
|
||||
@@ -0,0 +1,52 @@
|
||||
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
|
||||
# UV_PYTHON_INSTALL_DIR ensures Python is installed to a persistent location
|
||||
# that survives in the final image (not in /root/.local which gets lost)
|
||||
# UV_PYTHON_PREFERENCE=only-managed tells uv to use its managed Python version
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
UV_PYTHON_INSTALL_DIR=/python \
|
||||
UV_PYTHON_PREFERENCE=only-managed
|
||||
|
||||
# Create a group and user with the provided UID/GID
|
||||
# Check if the GID already exists, if not create appgroup
|
||||
RUN (getent group ${GID} || groupadd --gid ${GID} appgroup) && \
|
||||
useradd --uid ${UID} --gid ${GID} --create-home --shell /bin/bash appuser
|
||||
|
||||
# Copy the project into the image
|
||||
ADD . /app
|
||||
|
||||
# Install Python 3.13 explicitly and sync the project
|
||||
WORKDIR /app
|
||||
RUN uv python install 3.13
|
||||
RUN uv sync --locked --python 3.13
|
||||
|
||||
# Create necessary directories and set ownership
|
||||
RUN mkdir -p /app/data/basic-memory /app/.basic-memory && \
|
||||
chown -R appuser:${GID} /app
|
||||
|
||||
# Set default data directory and add venv to PATH
|
||||
ENV BASIC_MEMORY_HOME=/app/data/basic-memory \
|
||||
BASIC_MEMORY_PROJECT_ROOT=/app/data \
|
||||
PATH="/app/.venv/bin:$PATH"
|
||||
|
||||
# Switch to the non-root user
|
||||
USER appuser
|
||||
|
||||
# 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"]
|
||||
@@ -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)"
|
||||
@@ -0,0 +1,494 @@
|
||||
# Note Format Reference
|
||||
|
||||
Every document in Basic Memory is a plain Markdown file. Files are the source of truth — changes to files automatically update the knowledge graph in the database. You maintain complete ownership, files work with git, and knowledge persists independently of any AI conversation.
|
||||
|
||||
## Document Structure
|
||||
|
||||
A note has three parts: YAML frontmatter, content (observations), and relations.
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
type: note
|
||||
tags: [coffee, brewing]
|
||||
permalink: coffee-brewing-methods
|
||||
---
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
## Observations
|
||||
- [method] Pour over provides more flavor clarity than French press
|
||||
- [technique] Water temperature at 205°F extracts optimal compounds #brewing
|
||||
- [preference] Ethiopian beans work well with lighter roasts (personal experience)
|
||||
|
||||
## Relations
|
||||
- relates_to [[Coffee Bean Origins]]
|
||||
- requires [[Proper Grinding Technique]]
|
||||
- contrasts_with [[Tea Brewing Methods]]
|
||||
```
|
||||
|
||||
The `## Observations` and `## Relations` headings are conventional but not required — the parser detects observations and relations by their syntax patterns anywhere in the document.
|
||||
|
||||
## Frontmatter
|
||||
|
||||
YAML metadata between `---` fences at the top of the file.
|
||||
|
||||
| Field | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| `title` | No | filename stem | Used for linking and references. Auto-set from filename if missing. |
|
||||
| `type` | No | `note` | Entity type. Used for schema resolution and filtering. |
|
||||
| `tags` | No | `[]` | List or comma-separated string. Used for organization and search. |
|
||||
| `permalink` | No | generated from title | Stable identifier. Persists even if the file moves. |
|
||||
| `schema` | No | none | Schema attachment — dict (inline), string (reference), or omitted (implicit). |
|
||||
|
||||
Custom fields are allowed. Any key not in the standard set is stored as `entity_metadata` and indexed for search and filtering.
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Paul Graham
|
||||
type: Person
|
||||
tags: [startups, essays, lisp]
|
||||
permalink: paul-graham
|
||||
status: active
|
||||
source: wikipedia
|
||||
---
|
||||
```
|
||||
|
||||
Here `status` and `source` are custom fields stored in `entity_metadata`.
|
||||
|
||||
### Frontmatter Value Handling
|
||||
|
||||
YAML automatically converts some values to native types. Basic Memory normalizes them:
|
||||
|
||||
- Date strings (`2025-10-24`) → kept as ISO format strings
|
||||
- Numbers (`1.0`) → converted to strings
|
||||
- Booleans (`true`) → converted to strings (`"True"`)
|
||||
- Lists and dicts → preserved, items normalized recursively
|
||||
|
||||
This prevents errors when downstream code expects string values.
|
||||
|
||||
## Observations
|
||||
|
||||
An observation is a categorized fact about the entity. Written as a Markdown list item.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
```
|
||||
- [category] content text #tag1 #tag2 (context)
|
||||
```
|
||||
|
||||
| Part | Required | Description |
|
||||
|------|----------|-------------|
|
||||
| `[category]` | Yes | Classification in square brackets. Any text except `[]()` chars. |
|
||||
| content | Yes | The fact or statement. |
|
||||
| `#tags` | No | Inline tags. Space-separated, each starting with `#`. |
|
||||
| `(context)` | No | Parenthesized text at end of line. Supporting details or source. |
|
||||
|
||||
### Examples
|
||||
|
||||
```markdown
|
||||
- [tech] Uses SQLite for storage #database
|
||||
- [design] Follows local-first architecture #architecture
|
||||
- [decision] Selected bcrypt for passwords #security (based on OWASP audit)
|
||||
- [name] Paul Graham
|
||||
- [expertise] Startups
|
||||
- [expertise] Lisp
|
||||
- [expertise] Essay writing
|
||||
```
|
||||
|
||||
Array-like fields use repeated categories — multiple `[expertise]` observations above.
|
||||
|
||||
### What Is Not an Observation
|
||||
|
||||
The parser excludes these list item patterns:
|
||||
|
||||
| Pattern | Example | Reason |
|
||||
|---------|---------|--------|
|
||||
| Checkboxes | `- [ ] Todo item`, `- [x] Done`, `- [-] Cancelled` | Task list syntax |
|
||||
| Markdown links | `- [text](url)` | URL link syntax |
|
||||
| Bare wiki links | `- [[Target]]` | Treated as a relation instead |
|
||||
|
||||
A list item with `#tags` but no `[category]` is still parsed — the tags are extracted and the category defaults to `Note`.
|
||||
|
||||
## Relations
|
||||
|
||||
Relations connect documents to form the knowledge graph. There are two kinds.
|
||||
|
||||
### Explicit Relations
|
||||
|
||||
Written as list items with a relation type and a `[[wiki link]]` target.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
```
|
||||
- relation_type [[Target Entity]] (context)
|
||||
```
|
||||
|
||||
| Part | Required | Description |
|
||||
|------|----------|-------------|
|
||||
| `relation_type` | No | Text before `[[`. Defaults to `relates_to` if omitted. |
|
||||
| `[[Target]]` | Yes | Wiki link to the target entity. Matched by title or permalink. |
|
||||
| `(context)` | No | Parenthesized text after `]]`. Supporting details. |
|
||||
|
||||
### Examples
|
||||
|
||||
```markdown
|
||||
- implements [[Search Design]]
|
||||
- depends_on [[Database Schema]]
|
||||
- works_at [[Y Combinator]] (co-founder)
|
||||
- [[Some Entity]]
|
||||
```
|
||||
|
||||
The last example — a bare `[[wiki link]]` in a list item — gets relation type `relates_to`.
|
||||
|
||||
Common relation types:
|
||||
- `implements`, `depends_on`, `relates_to`, `inspired_by`
|
||||
- `extends`, `part_of`, `contains`, `pairs_with`
|
||||
- `works_at`, `authored`, `collaborated_with`
|
||||
|
||||
Any text works as a relation type. These are conventions, not a fixed set.
|
||||
|
||||
### Inline References
|
||||
|
||||
Wiki links appearing in regular prose (not as list items) create implicit `links_to` relations.
|
||||
|
||||
```markdown
|
||||
This builds on [[Core Design]] and uses [[Utility Functions]].
|
||||
```
|
||||
|
||||
This creates two relations: `links_to [[Core Design]]` and `links_to [[Utility Functions]]`.
|
||||
|
||||
### Forward References
|
||||
|
||||
Relations can link to entities that don't exist yet. Basic Memory resolves them when the target is created.
|
||||
|
||||
## Permalinks and memory:// URLs
|
||||
|
||||
Every document has a unique **permalink** — a stable identifier derived from its title. You can set one explicitly in frontmatter, or let the system generate it.
|
||||
|
||||
```yaml
|
||||
permalink: auth-approaches-2024
|
||||
```
|
||||
|
||||
Permalinks form the basis of `memory://` URLs:
|
||||
|
||||
```
|
||||
memory://auth-approaches-2024 # By permalink
|
||||
memory://Authentication Approaches # By title (auto-resolves)
|
||||
memory://project/auth-approaches # By path
|
||||
```
|
||||
|
||||
Pattern matching is supported:
|
||||
|
||||
```
|
||||
memory://auth* # Starts with "auth"
|
||||
memory://*/approaches # Ends with "approaches"
|
||||
memory://project/*/requirements # Nested wildcard
|
||||
```
|
||||
|
||||
## Schemas
|
||||
|
||||
Schemas declare the expected structure of a note — which observation categories and relation types a well-formed note should have. They use Picoschema, a compact notation from Google's Dotprompt that fits naturally in YAML frontmatter.
|
||||
|
||||
### Picoschema Syntax
|
||||
|
||||
```yaml
|
||||
schema:
|
||||
name: string, full name # required field with description
|
||||
email?: string, contact email # ? = optional
|
||||
role?: string, job title
|
||||
works_at?: Organization, employer # capitalized type = entity reference
|
||||
tags?(array): string, categories # array of type
|
||||
status?(enum): [active, inactive] # enum with allowed values
|
||||
metadata?(object): # nested object
|
||||
updated_at?: string
|
||||
source?: string
|
||||
```
|
||||
|
||||
| Notation | Meaning | Example |
|
||||
|----------|---------|---------|
|
||||
| `field: type` | Required field | `name: string` |
|
||||
| `field?: type` | Optional field | `role?: string` |
|
||||
| `field(array): type` | Array of values | `expertise(array): string` |
|
||||
| `field?(enum): [vals]` | Enum with allowed values | `status?(enum): [active, inactive]` |
|
||||
| `field?(object):` | Nested object with sub-fields | `metadata?(object):` |
|
||||
| `, description` | Description after comma | `name: string, full name` |
|
||||
| `EntityName` | Capitalized type = entity reference | `works_at?: Organization` |
|
||||
|
||||
**Scalar types:** `string`, `integer`, `number`, `boolean`, `any`
|
||||
|
||||
Any type not in that set whose first letter is uppercase is treated as an entity reference (a relation target).
|
||||
|
||||
### Schema-to-Note Mapping
|
||||
|
||||
Schemas validate against existing observation/relation syntax. Note authors don't learn new syntax.
|
||||
|
||||
| Schema Declaration | Maps To | Example in Note |
|
||||
|--------------------|---------|-----------------|
|
||||
| `field: string` | Observation `[field] value` | `- [name] Paul Graham` |
|
||||
| `field?(array): string` | Multiple `[field]` observations | `- [expertise] Lisp` (repeated) |
|
||||
| `field?: EntityType` | Relation `field [[Target]]` | `- works_at [[Y Combinator]]` |
|
||||
| `field?(array): EntityType` | Multiple `field` relations | `- authored [[Book]]` (repeated) |
|
||||
| `tags` | Frontmatter `tags` array | `tags: [startups, essays]` |
|
||||
| `field?(enum): [vals]` | Observation `[field] value` where value is in the set | `- [status] active` |
|
||||
|
||||
Observations and relations not covered by the schema are valid — schemas describe a subset, not a straitjacket.
|
||||
|
||||
### Schema Attachment
|
||||
|
||||
Three ways to attach a schema to a note, resolved in priority order:
|
||||
|
||||
**1. Inline schema** — `schema` is a dict in frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Team Standup 2024-01-15
|
||||
type: meeting
|
||||
schema:
|
||||
attendees(array): string, who was there
|
||||
decisions(array): string, what was decided
|
||||
action_items(array): string, follow-ups
|
||||
blockers?(array): string, anything stuck
|
||||
---
|
||||
```
|
||||
|
||||
Good for one-off structured notes or prototyping a schema before extracting it.
|
||||
|
||||
**2. Explicit reference** — `schema` is a string naming a schema note:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Basic Memory
|
||||
schema: SoftwareProject
|
||||
---
|
||||
```
|
||||
|
||||
or by permalink:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: LLM Memory Patterns
|
||||
schema: schema/research-project
|
||||
---
|
||||
```
|
||||
|
||||
Use when the note's `type` differs from the schema it should validate against, or when multiple schema variants exist.
|
||||
|
||||
**3. Implicit by type** — no `schema` field, resolved by matching `type`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Paul Graham
|
||||
type: Person
|
||||
---
|
||||
```
|
||||
|
||||
The system looks up a schema note where `entity: Person`. If found, it applies. If not, no validation occurs.
|
||||
|
||||
**4. No schema** — perfectly fine. Most notes don't need one.
|
||||
|
||||
### Schema Notes
|
||||
|
||||
A schema is itself a Basic Memory note with `type: schema`. It lives anywhere (though `schema/` is the conventional directory).
|
||||
|
||||
```yaml
|
||||
# schema/Person.md
|
||||
---
|
||||
title: Person
|
||||
type: schema
|
||||
entity: Person
|
||||
version: 1
|
||||
schema:
|
||||
name: string, full name
|
||||
role?: string, job title or position
|
||||
works_at?: Organization, employer
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
settings:
|
||||
validation: warn
|
||||
---
|
||||
|
||||
# Person
|
||||
|
||||
A human individual in the knowledge graph.
|
||||
```
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `type` | Yes | Must be `schema` |
|
||||
| `entity` | Yes | The entity type this schema describes (e.g., `Person`) |
|
||||
| `version` | No | Schema version number (default: `1`) |
|
||||
| `schema` | Yes | Picoschema dict defining the fields |
|
||||
| `settings.validation` | No | Validation mode (default: `warn`) |
|
||||
|
||||
Schema notes are regular notes — they show up in search, can have observations and relations, and participate in the knowledge graph.
|
||||
|
||||
### Validation Modes
|
||||
|
||||
| Mode | Behavior |
|
||||
|------|----------|
|
||||
| `warn` | Warnings in output, doesn't block (default) |
|
||||
| `strict` | Errors that block sync, for CI/CD enforcement |
|
||||
| `off` | No validation |
|
||||
|
||||
### Validation Output
|
||||
|
||||
```
|
||||
$ bm schema validate people/ada-lovelace.md
|
||||
|
||||
⚠ Person schema validation:
|
||||
- Missing required field: name (expected [name] observation)
|
||||
- Missing optional field: role
|
||||
- Missing optional field: works_at (no relation found)
|
||||
|
||||
ℹ Unmatched observations: [fact] ×2, [born] ×1
|
||||
ℹ Unmatched relations: collaborated_with
|
||||
```
|
||||
|
||||
"Unmatched" items are informational — observations and relations the schema doesn't cover.
|
||||
|
||||
### Schema Inference
|
||||
|
||||
Generate schemas from existing notes by analyzing observation and relation frequency:
|
||||
|
||||
```
|
||||
$ bm schema infer Person
|
||||
|
||||
Analyzing 30 notes with type: Person...
|
||||
|
||||
Observations found:
|
||||
[name] 30/30 100% → name: string
|
||||
[role] 27/30 90% → role?: string
|
||||
[expertise] 18/30 60% → expertise?(array): string
|
||||
[email] 8/30 27% → email?: string
|
||||
|
||||
Relations found:
|
||||
works_at 22/30 73% → works_at?: Organization
|
||||
|
||||
Suggested schema:
|
||||
name: string, full name
|
||||
role?: string, job title
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
works_at?: Organization, employer
|
||||
|
||||
Save to schema/Person.md? [y/n]
|
||||
```
|
||||
|
||||
Frequency thresholds:
|
||||
- **100% present** → required field
|
||||
- **25%+ present** → optional field
|
||||
- **Below 25%** → excluded from suggestion
|
||||
|
||||
### Schema Drift Detection
|
||||
|
||||
Track how usage patterns shift over time:
|
||||
|
||||
```
|
||||
$ bm schema diff Person
|
||||
|
||||
Schema drift detected:
|
||||
|
||||
+ expertise: now in 81% of notes (was 12%)
|
||||
- department: dropped to 3% of notes
|
||||
~ works_at: cardinality changed (one → many)
|
||||
|
||||
Update schema? [y/n/review]
|
||||
```
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Simple Note (No Schema)
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Project Ideas
|
||||
type: note
|
||||
tags: [ideas, brainstorm]
|
||||
---
|
||||
|
||||
# Project Ideas
|
||||
|
||||
## Observations
|
||||
- [idea] Build a CLI tool for markdown linting #tooling
|
||||
- [idea] Create a recipe knowledge base #cooking
|
||||
- [priority] Focus on developer tools first (Q1 goal)
|
||||
|
||||
## Relations
|
||||
- inspired_by [[Developer Workflow Research]]
|
||||
- part_of [[Q1 Planning]]
|
||||
```
|
||||
|
||||
### Schema-Validated Note
|
||||
|
||||
Schema at `schema/Person.md`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Person
|
||||
type: schema
|
||||
entity: Person
|
||||
version: 1
|
||||
schema:
|
||||
name: string, full name
|
||||
role?: string, job title or position
|
||||
works_at?: Organization, employer
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
settings:
|
||||
validation: warn
|
||||
---
|
||||
|
||||
# Person
|
||||
|
||||
A human individual in the knowledge graph.
|
||||
```
|
||||
|
||||
Note at `people/paul-graham.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Paul Graham
|
||||
type: Person
|
||||
tags: [startups, essays, lisp]
|
||||
---
|
||||
|
||||
# Paul Graham
|
||||
|
||||
## Observations
|
||||
- [name] Paul Graham
|
||||
- [role] Essayist and investor
|
||||
- [expertise] Startups
|
||||
- [expertise] Lisp
|
||||
- [expertise] Essay writing
|
||||
- [fact] Created Viaweb, the first web app
|
||||
|
||||
## Relations
|
||||
- works_at [[Y Combinator]]
|
||||
- authored [[Hackers and Painters]]
|
||||
```
|
||||
|
||||
The `[fact]` observation and `authored` relation are not in the schema — they're valid, just unmatched. The schema only checks that `[name]` exists (required) and looks for optional fields like `[role]`, `[expertise]`, and `works_at`.
|
||||
|
||||
### Inline Schema Note
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Team Standup 2024-01-15
|
||||
type: meeting
|
||||
schema:
|
||||
attendees(array): string, who was there
|
||||
decisions(array): string, what was decided
|
||||
action_items(array): string, follow-ups
|
||||
blockers?(array): string, anything stuck
|
||||
---
|
||||
|
||||
# Team Standup 2024-01-15
|
||||
|
||||
## Observations
|
||||
- [attendees] Paul
|
||||
- [attendees] Sarah
|
||||
- [decisions] Ship v2 by Friday
|
||||
- [action_items] Paul to review PR #42
|
||||
- [blockers] Waiting on API credentials
|
||||
```
|
||||
@@ -1,29 +1,45 @@
|
||||
# Basic Memory
|
||||
|
||||
<!-- mcp-name: io.github.basicmachines-co/basic-memory -->
|
||||
[](https://www.gnu.org/licenses/agpl-3.0)
|
||||
[](https://badge.fury.io/py/basic-memory)
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://github.com/basicmachines-co/basic-memory/actions)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||

|
||||

|
||||
|
||||
## 🚀 Basic Memory Cloud is Live!
|
||||
|
||||
- **Cross-device and multi-platform support is here.** Your knowledge graph now works on desktop, web, and mobile.
|
||||
- **Cloud is optional.** The local-first open-source workflow continues as always.
|
||||
- **OSS discount:** use code `BMFOSS` for 20% off for 3 months.
|
||||
|
||||
[Sign up now →](https://basicmemory.com?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
|
||||
with a 7 day free trial
|
||||
|
||||
# Basic Memory
|
||||
|
||||
Basic Memory lets you build persistent knowledge through natural conversations with Large Language Models (LLMs) like
|
||||
Claude, while keeping everything in simple Markdown files on your computer. It uses the Model Context Protocol (MCP) to
|
||||
enable any compatible LLM to read and write to your local knowledge base.
|
||||
|
||||
- Website: http://basicmachines.co
|
||||
- Documentation: http://memory.basicmachines.co
|
||||
- Website: [basicmemory.com](https://basicmemory.com?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
- Documentation: [docs.basicmemory.com](https://docs.basicmemory.com?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
- Community: [Discord](https://discord.gg/tyvKNccgqN?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
|
||||

|
||||
## Pick up your conversation right where you left off
|
||||
|
||||
Basic Memory provides persistent contextual awareness across sessions through a structured knowledge graph.
|
||||
The system enables LLMs to access and reference prior conversations, track semantic relationships between concepts, and
|
||||
incorporate human edits made directly to knowledge files.
|
||||
- AI assistants can load context from local files in a new conversation
|
||||
- Notes are saved locally as Markdown files in real time
|
||||
- No project knowledge or special prompting required
|
||||
|
||||
https://github.com/user-attachments/assets/a55d8238-8dd0-454a-be4c-8860dbbd0ddc
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Install with uv (recommended)
|
||||
uv install basic-memory
|
||||
uv tool install basic-memory
|
||||
|
||||
# Configure Claude Desktop (edit ~/Library/Application Support/Claude/claude_desktop_config.json)
|
||||
# Add this to your config:
|
||||
@@ -47,34 +63,6 @@ uv install basic-memory
|
||||
|
||||
You can view shared context via files in `~/basic-memory` (default directory location).
|
||||
|
||||
You can also install the cli tools to sync files or manage projects.
|
||||
|
||||
```bash
|
||||
uv tool install basic-memory
|
||||
|
||||
# create a new project in a different directory
|
||||
basic-memory project add coffee ./examples/coffee
|
||||
|
||||
# you can set the project to the default
|
||||
basic-memory project default coffee
|
||||
```
|
||||
|
||||
View available projects
|
||||
|
||||
```bash
|
||||
basic-memory project list
|
||||
Basic Memory Projects
|
||||
┏━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┓
|
||||
┃ Name ┃ Path ┃ Default ┃ Active ┃
|
||||
┡━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━┩
|
||||
│ main │ ~/basic-memory │ ✓ │ ✓ │
|
||||
│ coffee │ ~/dev/basicmachines/basic-memory/examples/coffee │ │ │
|
||||
└────────┴──────────────────────────────────────────────────┴─────────┴────────┘
|
||||
```
|
||||
|
||||
Basic Memory will write notes in Markdown format. Open you project directory in your text editor to view project files
|
||||
while you have conversations with an LLM.
|
||||
|
||||
## Why Basic Memory?
|
||||
|
||||
Most LLM interactions are ephemeral - you ask a question, get an answer, and everything is forgotten. Each conversation
|
||||
@@ -105,6 +93,9 @@ With Basic Memory, you can:
|
||||
- Keep everything local and under your control
|
||||
- Use familiar tools like Obsidian to view and edit notes
|
||||
- Build a personal knowledge base that grows over time
|
||||
- Sync your knowledge to the cloud with bidirectional synchronization
|
||||
- Authenticate and manage cloud projects with subscription validation
|
||||
- Mount cloud storage for direct file access
|
||||
|
||||
## How It Works in Practice
|
||||
|
||||
@@ -154,36 +145,13 @@ tags:
|
||||
- affects [[Flavor Extraction]]
|
||||
```
|
||||
|
||||
The note embeds semantic content and links to other topics via simple Markdown
|
||||
formatting.
|
||||
The note embeds semantic content and links to other topics via simple Markdown formatting.
|
||||
|
||||
3. You see this file on your computer in real time in the `~/$HOME/basic-memory` directory:
|
||||
3. You see this file on your computer in real time in the current project directory (default `~/$HOME/basic-memory`).
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
permalink: coffee-brewing-methods
|
||||
type: note
|
||||
---
|
||||
- Realtime sync can be enabled via running `basic-memory sync --watch`
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
## Observations
|
||||
|
||||
- [method] Pour over provides more clarity and highlights subtle flavors
|
||||
- [technique] Water temperature at 205°F (96°C) extracts optimal compounds
|
||||
- [principle] Freshly ground beans preserve aromatics and flavor
|
||||
- [preference] Medium-light roasts work best for pour over # Added by you
|
||||
|
||||
## Relations
|
||||
|
||||
- relates_to [[Coffee Bean Origins]]
|
||||
- requires [[Proper Grinding Technique]]
|
||||
- affects [[Flavor Extraction]]
|
||||
- pairs_with [[Breakfast Pastries]] # Added by you
|
||||
```
|
||||
|
||||
4. In a new chat with the LLM, you can reference this knowledge:
|
||||
4. In a chat with the LLM, you can reference a topic:
|
||||
|
||||
```
|
||||
Look at `coffee-brewing-methods` for context about pour over coffee
|
||||
@@ -203,15 +171,14 @@ Following relation 'requires [[Proper Grinding Technique]]':
|
||||
- Impact of consistent particle size on extraction
|
||||
```
|
||||
|
||||
Each related document can lead to more context, building a rich semantic understanding of your knowledge base. All of
|
||||
this context comes from standard Markdown files that both humans and LLMs can read and write.
|
||||
Each related document can lead to more context, building a rich semantic understanding of your knowledge base.
|
||||
|
||||
Every time the LLM writes notes,they are saved in local Markdown files that you can:
|
||||
This creates a two-way flow where:
|
||||
|
||||
- Edit in any text editor
|
||||
- Version via git
|
||||
- Back up normally
|
||||
- Share when you want to
|
||||
- Humans write and edit Markdown files
|
||||
- LLMs read and write through the MCP protocol
|
||||
- Sync keeps everything consistent
|
||||
- All knowledge stays in local files.
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
@@ -247,7 +214,7 @@ permalink: <a uri slug>
|
||||
|
||||
Observations are facts about a topic.
|
||||
They can be added by creating a Markdown list with a special format that can reference a `category`, `tags` using a
|
||||
"#" charactor, and an optional `context`.
|
||||
"#" character, and an optional `context`.
|
||||
|
||||
Observation Markdown format:
|
||||
|
||||
@@ -291,72 +258,37 @@ Examples of relations:
|
||||
- documented_in [[Coffee Journal]]
|
||||
```
|
||||
|
||||
### Complete Example
|
||||
## Using with VS Code
|
||||
|
||||
Here's a complete example of a note with frontmatter, observations, and relations:
|
||||
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)`.
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Pour Over Coffee Method
|
||||
type: note
|
||||
permalink: pour-over-coffee-method
|
||||
tags:
|
||||
- brewing
|
||||
- coffee
|
||||
- techniques
|
||||
---
|
||||
|
||||
# Pour Over Coffee Method
|
||||
|
||||
This note documents the pour over brewing method and my experiences with it.
|
||||
|
||||
## Overview
|
||||
|
||||
The pour over method involves pouring hot water through coffee grounds in a filter. The water drains through the coffee
|
||||
and filter into a carafe or cup.
|
||||
|
||||
## Observations
|
||||
|
||||
- [equipment] Hario V60 dripper produces clean, bright cup #gear
|
||||
- [technique] Pour in concentric circles to ensure even extraction
|
||||
- [ratio] 1:16 coffee-to-water ratio works best for balanced flavor
|
||||
- [timing] Total brew time should be 2:30-3:00 minutes for medium roast
|
||||
- [temperature] Water at 205°F (96°C) extracts optimal flavor compounds
|
||||
- [grind] Medium-fine grind similar to table salt texture
|
||||
- [tip] 30-45 second bloom with double the coffee weight in water
|
||||
- [result] Produces a cleaner cup with more distinct flavor notes than immersion methods
|
||||
|
||||
## Relations
|
||||
|
||||
- complements [[Light Roast Beans]]
|
||||
- requires [[Gooseneck Kettle]]
|
||||
- contrasts_with [[French Press Method]]
|
||||
- pairs_with [[Breakfast Pastries]]
|
||||
- documented_in [[Brewing Journal]]
|
||||
- inspired_by [[Japanese Brewing Techniques]]
|
||||
- affects [[Flavor Extraction]]
|
||||
- part_of [[Morning Ritual]]
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"basic-memory": {
|
||||
"command": "uvx",
|
||||
"args": ["basic-memory", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Basic Memory will parse the Markdown and derive the semantic relationships in the content. When you run
|
||||
`basic-memory sync`:
|
||||
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.
|
||||
|
||||
1. New and changed files are detected
|
||||
2. Markdown patterns become semantic knowledge:
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"basic-memory": {
|
||||
"command": "uvx",
|
||||
"args": ["basic-memory", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `[tech]` becomes a categorized observation
|
||||
- `[[WikiLink]]` creates a relation in the knowledge graph
|
||||
- Tags and metadata are indexed for search
|
||||
|
||||
3. A SQLite database maintains these relationships for fast querying
|
||||
4. MCP-compatible LLMs can access this knowledge via memory:// URLs
|
||||
|
||||
This creates a two-way flow where:
|
||||
|
||||
- Humans write and edit Markdown files
|
||||
- LLMs read and write through the MCP protocol
|
||||
- Sync keeps everything consistent
|
||||
- All knowledge stays in local files.
|
||||
You can use Basic Memory with VS Code to easily retrieve and store information while coding.
|
||||
|
||||
## Using with Claude Desktop
|
||||
|
||||
@@ -410,14 +342,125 @@ basic-memory sync
|
||||
basic-memory sync --watch
|
||||
```
|
||||
|
||||
3. In Claude Desktop, the LLM can now use these tools:
|
||||
3. Cloud features (optional, requires subscription):
|
||||
|
||||
```bash
|
||||
# Authenticate with cloud (stores OAuth token locally)
|
||||
basic-memory cloud login
|
||||
|
||||
# (Optional) install/configure rclone for file sync commands
|
||||
basic-memory cloud setup
|
||||
|
||||
# Check cloud auth + health
|
||||
basic-memory cloud status
|
||||
```
|
||||
|
||||
**Per-Project Cloud Routing** (API key based):
|
||||
|
||||
Individual projects can be routed through the cloud while others stay local. This uses an API key for routed
|
||||
project calls:
|
||||
|
||||
```bash
|
||||
# Save an API key (create one in the web app or via CLI)
|
||||
basic-memory cloud set-key bmc_abc123...
|
||||
# Or create one via CLI (requires OAuth login first)
|
||||
basic-memory cloud create-key "my-laptop"
|
||||
|
||||
# Set a project to route through cloud
|
||||
basic-memory project set-cloud research
|
||||
|
||||
# Revert a project to local mode
|
||||
basic-memory project set-local research
|
||||
|
||||
# List projects and route metadata
|
||||
basic-memory project list
|
||||
```
|
||||
|
||||
`basic-memory cloud login` / `basic-memory cloud logout` are authentication commands. They do not change default CLI
|
||||
routing behavior.
|
||||
|
||||
**Routing Flags**:
|
||||
|
||||
Use routing flags to disambiguate command targets:
|
||||
|
||||
```bash
|
||||
# Force local routing for this command
|
||||
basic-memory status --local
|
||||
basic-memory project list --local
|
||||
basic-memory project ls --name main --local
|
||||
|
||||
# Force cloud routing for this command
|
||||
basic-memory status --cloud
|
||||
basic-memory project info my-project --cloud
|
||||
basic-memory project ls --name main --cloud
|
||||
```
|
||||
|
||||
No-flag behavior defaults to local when no project context is present.
|
||||
|
||||
The local MCP server routes per transport: `--transport stdio` honors per-project routing
|
||||
(local or cloud), while `--transport streamable-http` and `--transport sse` always route locally.
|
||||
|
||||
**CLI Note Editing (`tool edit-note`):**
|
||||
|
||||
```bash
|
||||
# Append content
|
||||
basic-memory tool edit-note project-plan --operation append --content $'\n## Next Steps\n- Finalize rollout'
|
||||
|
||||
# Find/replace with replacement count validation
|
||||
basic-memory tool edit-note docs/api --operation find_replace --find-text "v0.14.0" --content "v0.15.0" --expected-replacements 2
|
||||
|
||||
# Replace a section body
|
||||
basic-memory tool edit-note docs/setup --operation replace_section --section "## Installation" --content $'Updated install steps\n- Run just install'
|
||||
|
||||
# JSON metadata output for integrations
|
||||
basic-memory tool edit-note docs/setup --operation append --content $'\n- Added note' --format json
|
||||
```
|
||||
|
||||
4. In Claude Desktop, the LLM can now use these tools:
|
||||
|
||||
**Content Management:**
|
||||
```
|
||||
write_note(title, content, folder, tags, output_format="text"|"json") - Create or update notes
|
||||
read_note(identifier, page, page_size, output_format="text"|"json") - Read notes by title or permalink
|
||||
read_content(path) - Read raw file content (text, images, binaries)
|
||||
view_note(identifier) - View notes as formatted artifacts
|
||||
edit_note(identifier, operation, content, output_format="text"|"json") - Edit notes incrementally
|
||||
move_note(identifier, destination_path, output_format="text"|"json") - Move notes with database consistency
|
||||
delete_note(identifier, output_format="text"|"json") - Delete notes from knowledge base
|
||||
```
|
||||
|
||||
**Knowledge Graph Navigation:**
|
||||
```
|
||||
build_context(url, depth, timeframe, output_format="json"|"text") - Navigate knowledge graph via memory:// URLs
|
||||
recent_activity(type, depth, timeframe, output_format="text"|"json") - Find recently updated information
|
||||
list_directory(dir_name, depth) - Browse directory contents with filtering
|
||||
```
|
||||
|
||||
**Search & Discovery:**
|
||||
```
|
||||
write_note(title, content, folder, tags) - Create or update notes
|
||||
read_note(identifier, page, page_size) - Read notes by title or permalink
|
||||
build_context(url, depth, timeframe) - Navigate knowledge graph via memory:// URLs
|
||||
search(query, page, page_size) - Search across your knowledge base
|
||||
recent_activity(type, depth, timeframe) - Find recently updated information
|
||||
search_notes(query, page, page_size, search_type, types, entity_types, after_date, metadata_filters, tags, status, project) - Search with filters (query is optional for filter-only searches)
|
||||
```
|
||||
|
||||
**Project Management:**
|
||||
```
|
||||
list_memory_projects(output_format="text"|"json") - List all available projects
|
||||
create_memory_project(project_name, project_path, output_format="text"|"json") - Create new projects
|
||||
get_current_project() - Show current project stats
|
||||
sync_status() - Check synchronization status
|
||||
```
|
||||
|
||||
`output_format` defaults to `"text"` for these tools, preserving current human-readable responses.
|
||||
`build_context` defaults to `"json"` and can be switched to `"text"` when compact markdown output is preferred.
|
||||
|
||||
**Cloud Discovery (opt-in):**
|
||||
```
|
||||
cloud_info() - Show optional Cloud overview and setup guidance
|
||||
release_notes() - Show latest release notes
|
||||
```
|
||||
|
||||
**Visualization:**
|
||||
```
|
||||
canvas(nodes, edges, title, folder) - Generate knowledge visualizations
|
||||
```
|
||||
|
||||
@@ -431,263 +474,165 @@ canvas(nodes, edges, title, folder) - Generate knowledge visualizations
|
||||
"What have I been working on in the past week?"
|
||||
```
|
||||
|
||||
## Multiple Projects
|
||||
## Futher info
|
||||
|
||||
Basic Memory supports managing multiple separate knowledge bases through projects. This feature allows you to maintain
|
||||
separate knowledge graphs for different purposes (e.g., personal notes, work projects, research topics).
|
||||
See the [Documentation](https://docs.basicmemory.com?utm_source=github&utm_medium=referral&utm_campaign=readme) for more info, including:
|
||||
|
||||
### Managing Projects
|
||||
- [Complete User Guide](https://docs.basicmemory.com/user-guide/?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
- [CLI tools](https://docs.basicmemory.com/guides/cli-reference/?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
- [Cloud CLI and Sync](https://docs.basicmemory.com/guides/cloud-cli/?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
- [Managing multiple Projects](https://docs.basicmemory.com/guides/cli-reference/?utm_source=github&utm_medium=referral&utm_campaign=readme#project)
|
||||
- [Importing data from OpenAI/Claude Projects](https://docs.basicmemory.com/guides/cli-reference/?utm_source=github&utm_medium=referral&utm_campaign=readme#import)
|
||||
|
||||
## Telemetry
|
||||
|
||||
Basic Memory collects anonymous, minimal usage events to understand how the CLI-to-cloud conversion funnel performs. This helps us prioritize features and improve the product.
|
||||
|
||||
**What we collect:**
|
||||
- Cloud promo impressions (when the promo banner is shown)
|
||||
- Cloud login attempts and outcomes
|
||||
- Promo opt-out events
|
||||
|
||||
**What we do NOT collect:**
|
||||
- No file contents, note titles, or knowledge base data
|
||||
- No personally identifiable information (PII)
|
||||
- No IP address tracking or fingerprinting
|
||||
- No per-command or per-tool-call tracking
|
||||
|
||||
Events are sent to our [Umami Cloud](https://umami.is) instance, an open-source, privacy-focused analytics platform. Events are fire-and-forget on a background thread — analytics never blocks or slows the CLI.
|
||||
|
||||
**Opt out** by setting the environment variable:
|
||||
|
||||
```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
|
||||
export BASIC_MEMORY_NO_PROMOS=1
|
||||
```
|
||||
|
||||
### Using Projects in Commands
|
||||
This disables both promo messages and all telemetry events.
|
||||
|
||||
All commands support the `--project` flag to specify which project to use:
|
||||
## Logging
|
||||
|
||||
Basic Memory uses [Loguru](https://github.com/Delgan/loguru) for logging. The logging behavior varies by entry point:
|
||||
|
||||
| Entry Point | Default Behavior | Use Case |
|
||||
|-------------|------------------|----------|
|
||||
| CLI commands | File only | Prevents log output from interfering with command output |
|
||||
| MCP server | File only | Stdout would corrupt the JSON-RPC protocol |
|
||||
| API server | File (local) or stdout (cloud) | Docker/cloud deployments use stdout |
|
||||
|
||||
**Log file location:** `~/.basic-memory/basic-memory.log` (10MB rotation, 10 days retention)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `BASIC_MEMORY_LOG_LEVEL` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR |
|
||||
| `BASIC_MEMORY_CLOUD_MODE` | `false` | When `true`, API logs to stdout with structured context |
|
||||
| `BASIC_MEMORY_FORCE_LOCAL` | `false` | When `true`, forces local API routing |
|
||||
| `BASIC_MEMORY_FORCE_CLOUD` | `false` | When `true`, forces cloud API routing |
|
||||
| `BASIC_MEMORY_EXPLICIT_ROUTING` | `false` | When `true`, marks route selection as explicit (`--local`/`--cloud`) |
|
||||
| `BASIC_MEMORY_ENV` | `dev` | Set to `test` for test mode (stderr only) |
|
||||
| `BASIC_MEMORY_NO_PROMOS` | `false` | When `true`, disables cloud promo messages and telemetry |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Sync a specific project
|
||||
basic-memory --project=work sync
|
||||
# Enable debug logging
|
||||
BASIC_MEMORY_LOG_LEVEL=DEBUG basic-memory sync
|
||||
|
||||
# Run MCP server for a specific project
|
||||
basic-memory --project=personal mcp
|
||||
# View logs
|
||||
tail -f ~/.basic-memory/basic-memory.log
|
||||
|
||||
# Cloud/Docker mode (stdout logging with structured context)
|
||||
BASIC_MEMORY_CLOUD_MODE=true uvicorn basic_memory.api.app:app
|
||||
```
|
||||
|
||||
You can also set the `BASIC_MEMORY_PROJECT` environment variable:
|
||||
## Development
|
||||
|
||||
### Running Tests
|
||||
|
||||
Basic Memory supports dual database backends (SQLite and Postgres). By default, tests run against SQLite. Set `BASIC_MEMORY_TEST_POSTGRES=1` to run against Postgres (uses testcontainers - Docker required).
|
||||
|
||||
**Quick Start:**
|
||||
```bash
|
||||
BASIC_MEMORY_PROJECT=work basic-memory sync
|
||||
# Run all tests against SQLite (default, fast)
|
||||
just test-sqlite
|
||||
|
||||
# Run all tests against Postgres (uses testcontainers)
|
||||
just test-postgres
|
||||
|
||||
# Run both SQLite and Postgres tests
|
||||
just test
|
||||
```
|
||||
|
||||
### Project Isolation
|
||||
**Available Test Commands:**
|
||||
|
||||
Each project maintains:
|
||||
- `just test` - Run all tests against both SQLite and Postgres
|
||||
- `just test-sqlite` - Run all tests against SQLite (fast, no Docker needed)
|
||||
- `just test-postgres` - Run all tests against Postgres (uses testcontainers)
|
||||
- `just test-unit-sqlite` - Run unit tests against SQLite
|
||||
- `just test-unit-postgres` - Run unit tests against Postgres
|
||||
- `just test-int-sqlite` - Run integration tests against SQLite
|
||||
- `just test-int-postgres` - Run integration tests against Postgres
|
||||
- `just test-windows` - Run Windows-specific tests (auto-skips on other platforms)
|
||||
- `just test-benchmark` - Run performance benchmark tests
|
||||
- `just testmon` - Run tests impacted by recent changes (pytest-testmon)
|
||||
- `just test-smoke` - Run fast MCP end-to-end smoke test
|
||||
- `just fast-check` - Run fix/format/typecheck + impacted tests + smoke test
|
||||
- `just doctor` - Run local file <-> DB consistency checks with temp config
|
||||
|
||||
- Its own collection of markdown files in the specified directory
|
||||
- A separate SQLite database for that project
|
||||
- Complete knowledge graph isolation from other projects
|
||||
**Postgres Testing:**
|
||||
|
||||
## Design Philosophy
|
||||
Postgres tests use [testcontainers](https://testcontainers-python.readthedocs.io/) which automatically spins up a Postgres instance in Docker. No manual database setup required - just have Docker running.
|
||||
|
||||
Basic Memory is built on some key ideas:
|
||||
**Testmon Note:** When no files have changed, `just testmon` may collect 0 tests. That's expected and means no impacted tests were detected.
|
||||
|
||||
- Your knowledge should stay in files you control
|
||||
- Both humans and AI should use natural formats
|
||||
- Simple text patterns can capture rich meaning
|
||||
- Local-first doesn't mean feature-poor
|
||||
- Knowledge should persist across conversations
|
||||
- AI assistants should build on past context
|
||||
- File formats should be human-readable and editable
|
||||
- Semantic structure should emerge from natural patterns
|
||||
- Knowledge graphs should be both AI and human navigable
|
||||
- Systems should augment human memory, not replace it
|
||||
**Test Markers:**
|
||||
|
||||
## Importing Existing Data
|
||||
|
||||
Basic Memory provides CLI commands to import data from various sources, converting them into the structured Markdown
|
||||
format:
|
||||
|
||||
### Claude.ai
|
||||
|
||||
First, request an export of your data from your Claude account. The data will be emailed to you in several files,
|
||||
including
|
||||
`conversations.json` and `projects.json`.
|
||||
|
||||
Import Claude.ai conversation data
|
||||
Tests use pytest markers for selective execution:
|
||||
- `windows` - Windows-specific database optimizations
|
||||
- `benchmark` - Performance tests (excluded from default runs)
|
||||
- `smoke` - Fast MCP end-to-end smoke tests
|
||||
|
||||
**Other Development Commands:**
|
||||
```bash
|
||||
basic-memory import claude conversations
|
||||
just install # Install with dev dependencies
|
||||
just lint # Run linting checks
|
||||
just typecheck # Run type checking
|
||||
just typecheck-ty # Run ty type checking (incremental supplement to pyright)
|
||||
just format # Format code with ruff
|
||||
just fast-check # Fast local loop (fix/format/typecheck + testmon + smoke)
|
||||
just doctor # Local consistency check (temp config)
|
||||
just check # Run all quality checks
|
||||
just migration "msg" # Create database migration
|
||||
```
|
||||
|
||||
The conversations will be turned into Markdown files and placed in the "conversations" folder by default (this can be
|
||||
changed with the --folder arg).
|
||||
|
||||
Example:
|
||||
**Type Checking Strategy:**
|
||||
- `just typecheck` (Pyright) remains the primary, blocking type checker.
|
||||
- `just typecheck-ty` (Astral `ty`) is available as a supplemental checker while rules are adopted incrementally.
|
||||
- We recommend running both locally while reducing `ty` diagnostics over time.
|
||||
|
||||
**Local Consistency Check:**
|
||||
```bash
|
||||
Importing chats from conversations.json...writing to .../basic-memory
|
||||
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
||||
╭────────────────────────────╮
|
||||
│ Import complete! │
|
||||
│ │
|
||||
│ Imported 307 conversations │
|
||||
│ Containing 7769 messages │
|
||||
╰────────────────────────────╯
|
||||
basic-memory doctor # Verifies file <-> database sync in a temp project
|
||||
```
|
||||
|
||||
Next, you can run the `sync` command to import the data into basic-memory
|
||||
|
||||
```bash
|
||||
basic-memory sync
|
||||
```
|
||||
|
||||
You can also import project data from Claude.ai
|
||||
|
||||
```bash
|
||||
➜ basic-memory import claude projects
|
||||
Importing projects from projects.json...writing to .../basic-memory/projects
|
||||
Reading project data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
||||
╭────────────────────────────────╮
|
||||
│ Import complete! │
|
||||
│ │
|
||||
│ Imported 101 project documents │
|
||||
│ Imported 32 prompt templates │
|
||||
╰────────────────────────────────╯
|
||||
|
||||
Run 'basic-memory sync' to index the new files.
|
||||
```
|
||||
|
||||
### OpenAI ChatGPT
|
||||
|
||||
```bash
|
||||
➜ basic-memory import chatgpt
|
||||
Importing chats from conversations.json...writing to .../basic-memory/conversations
|
||||
|
||||
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
||||
╭────────────────────────────╮
|
||||
│ Import complete! │
|
||||
│ │
|
||||
│ Imported 198 conversations │
|
||||
│ Containing 11777 messages │
|
||||
╰────────────────────────────╯
|
||||
|
||||
|
||||
```
|
||||
|
||||
### Knowledge Graph Memory Server
|
||||
|
||||
From the MCP Server: https://github.com/modelcontextprotocol/servers/tree/main/src/memory
|
||||
|
||||
```bash
|
||||
➜ basic-memory import memory-json
|
||||
Importing from memory.json...writing to .../basic-memory
|
||||
Reading memory.json... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
||||
Creating entities... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
||||
╭──────────────────────╮
|
||||
│ Import complete! │
|
||||
│ │
|
||||
│ Created 126 entities │
|
||||
│ Added 252 relations │
|
||||
╰──────────────────────╯
|
||||
```
|
||||
|
||||
## Working with Your Knowledge Base
|
||||
|
||||
Once you've built up a knowledge base, you can interact with it in several ways:
|
||||
|
||||
### Command Line Interface
|
||||
|
||||
Basic Memory provides a powerful CLI for managing your knowledge:
|
||||
|
||||
```bash
|
||||
# See all available commands
|
||||
basic-memory --help
|
||||
|
||||
# Check the status of your knowledge sync
|
||||
basic-memory status
|
||||
|
||||
# Access specific tool functionality directly
|
||||
basic-memory tools
|
||||
|
||||
# Start a continuous sync process
|
||||
basic-memory sync --watch
|
||||
```
|
||||
|
||||
### Obsidian Integration
|
||||
|
||||
Basic Memory works seamlessly with [Obsidian](https://obsidian.md/), a popular knowledge management app:
|
||||
|
||||
1. Point Obsidian to your Basic Memory directory
|
||||
2. Use standard Obsidian features like backlinks and graph view
|
||||
3. See your knowledge graph visually
|
||||
4. Use the canvas visualization generated by Basic Memory
|
||||
|
||||
### File Organization
|
||||
|
||||
Basic Memory is flexible about how you organize your files:
|
||||
|
||||
- Group by topic in folders
|
||||
- Use a flat structure with descriptive filenames
|
||||
- Add custom metadata in frontmatter
|
||||
- Tag files for better searchability
|
||||
|
||||
The system will build the semantic knowledge graph regardless of your file organization preference.
|
||||
|
||||
## Using stdin with Basic Memory's `write_note` Tool
|
||||
|
||||
The `write-note` tool supports reading content from standard input (stdin), allowing for more flexible workflows when
|
||||
creating or updating notes in your Basic Memory knowledge base.
|
||||
|
||||
### Use Cases
|
||||
|
||||
This feature is particularly useful for:
|
||||
|
||||
1. **Piping output from other commands** directly into Basic Memory notes
|
||||
2. **Creating notes with multi-line content** without having to escape quotes or special characters
|
||||
3. **Integrating with AI assistants** like Claude Code that can generate content and pipe it to Basic Memory
|
||||
4. **Processing text data** from files or other sources
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Method 1: Using a Pipe
|
||||
|
||||
You can pipe content from another command into `write_note`:
|
||||
|
||||
```bash
|
||||
# Pipe output of a command into a new note
|
||||
echo "# My Note\n\nThis is a test note" | basic-memory tools write-note --title "Test Note" --folder "notes"
|
||||
|
||||
# Pipe output of a file into a new note
|
||||
cat README.md | basic-memory tools write-note --title "Project README" --folder "documentation"
|
||||
|
||||
# Process text through other tools before saving as a note
|
||||
cat data.txt | grep "important" | basic-memory tools write-note --title "Important Data" --folder "data"
|
||||
```
|
||||
|
||||
### Method 2: Using Heredoc Syntax
|
||||
|
||||
For multi-line content, you can use heredoc syntax:
|
||||
|
||||
```bash
|
||||
# Create a note with heredoc
|
||||
cat << EOF | basic-memory tools write_note --title "Project Ideas" --folder "projects"
|
||||
# Project Ideas for Q2
|
||||
|
||||
## AI Integration
|
||||
- Improve recommendation engine
|
||||
- Add semantic search to product catalog
|
||||
|
||||
## Infrastructure
|
||||
- Migrate to Kubernetes
|
||||
- Implement CI/CD pipeline
|
||||
EOF
|
||||
```
|
||||
|
||||
### Method 3: Input Redirection
|
||||
|
||||
You can redirect input from a file:
|
||||
|
||||
```bash
|
||||
# Create a note from file content
|
||||
basic-memory tools write-note --title "Meeting Notes" --folder "meetings" < meeting_notes.md
|
||||
```
|
||||
See the [justfile](justfile) for the complete list of development commands.
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0
|
||||
|
||||
Built with ♥️ by Basic Machines
|
||||
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](https://basicmachines.co?utm_source=github&utm_medium=referral&utm_campaign=readme)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.x.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Use this section to tell people how to report a vulnerability.
|
||||
|
||||
If you find a vulnerability, please contact hello@basicmachines.co
|
||||
@@ -0,0 +1,42 @@
|
||||
# Docker Compose configuration for Basic Memory with PostgreSQL
|
||||
# Use this for local development and testing with Postgres backend
|
||||
#
|
||||
# Usage:
|
||||
# docker-compose -f docker-compose-postgres.yml up -d
|
||||
# docker-compose -f docker-compose-postgres.yml down
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17
|
||||
container_name: basic-memory-postgres
|
||||
environment:
|
||||
# Local development/test credentials - NOT for production
|
||||
# These values are referenced by tests and justfile commands
|
||||
POSTGRES_DB: basic_memory
|
||||
POSTGRES_USER: basic_memory_user
|
||||
POSTGRES_PASSWORD: dev_password # Simple password for local testing only
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U basic_memory_user -d basic_memory"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
# Named volume for Postgres data
|
||||
postgres_data:
|
||||
driver: local
|
||||
|
||||
# Named volume for persistent configuration
|
||||
# Database will be stored in Postgres, not in this volume
|
||||
basic-memory-config:
|
||||
driver: local
|
||||
|
||||
# Network configuration (optional)
|
||||
# networks:
|
||||
# basic-memory-net:
|
||||
# driver: bridge
|
||||
@@ -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
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -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,6 +0,0 @@
|
||||
{
|
||||
"siteId": "947ee055a8c6f1a57efa4afa09791e62",
|
||||
"host": "publish-01.obsidian.md",
|
||||
"included": [],
|
||||
"excluded": []
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,442 @@
|
||||
# Basic Memory Architecture
|
||||
|
||||
This document describes the architectural patterns and composition structure of Basic Memory.
|
||||
|
||||
## Overview
|
||||
|
||||
Basic Memory is a local-first knowledge management system with three entrypoints:
|
||||
- **API** - FastAPI REST server for HTTP access
|
||||
- **MCP** - Model Context Protocol server for LLM integration
|
||||
- **CLI** - Typer command-line interface
|
||||
|
||||
Each entrypoint uses a **composition root** pattern to manage configuration and dependencies.
|
||||
|
||||
## Composition Roots
|
||||
|
||||
### What is a Composition Root?
|
||||
|
||||
A composition root is the single place in an application where dependencies are wired together. In Basic Memory, each entrypoint has its own composition root that:
|
||||
|
||||
1. Reads configuration from `ConfigManager`
|
||||
2. Resolves runtime mode (local/test)
|
||||
3. Creates and provides dependencies to downstream code
|
||||
|
||||
**Key principle**: Only composition roots read global configuration. All other modules receive configuration explicitly.
|
||||
|
||||
### Container Structure
|
||||
|
||||
Each entrypoint has a container dataclass in its package:
|
||||
|
||||
```
|
||||
src/basic_memory/
|
||||
├── api/
|
||||
│ └── container.py # ApiContainer
|
||||
├── mcp/
|
||||
│ └── container.py # McpContainer
|
||||
├── cli/
|
||||
│ └── container.py # CliContainer
|
||||
└── runtime.py # RuntimeMode enum and resolver
|
||||
```
|
||||
|
||||
### Container Pattern
|
||||
|
||||
All containers follow the same structure:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Container:
|
||||
config: BasicMemoryConfig
|
||||
mode: RuntimeMode
|
||||
|
||||
@classmethod
|
||||
def create(cls) -> "Container":
|
||||
"""Create container by reading ConfigManager."""
|
||||
config = ConfigManager().config
|
||||
mode = resolve_runtime_mode(is_test_env=config.is_test_env)
|
||||
return cls(config=config, mode=mode)
|
||||
|
||||
@property
|
||||
def some_computed_property(self) -> bool:
|
||||
"""Derived values based on config and mode."""
|
||||
return self.mode.is_local and self.config.some_setting
|
||||
|
||||
# Module-level singleton
|
||||
_container: Container | None = None
|
||||
|
||||
def get_container() -> Container:
|
||||
if _container is None:
|
||||
raise RuntimeError("Container not initialized")
|
||||
return _container
|
||||
|
||||
def set_container(container: Container) -> None:
|
||||
global _container
|
||||
_container = container
|
||||
```
|
||||
|
||||
### Runtime Mode Resolution
|
||||
|
||||
The `RuntimeMode` enum centralizes mode detection:
|
||||
|
||||
```python
|
||||
class RuntimeMode(Enum):
|
||||
LOCAL = "local"
|
||||
CLOUD = "cloud"
|
||||
TEST = "test"
|
||||
|
||||
@property
|
||||
def is_cloud(self) -> bool:
|
||||
return self == RuntimeMode.CLOUD
|
||||
|
||||
@property
|
||||
def is_local(self) -> bool:
|
||||
return self == RuntimeMode.LOCAL
|
||||
|
||||
@property
|
||||
def is_test(self) -> bool:
|
||||
return self == RuntimeMode.TEST
|
||||
```
|
||||
|
||||
Resolution follows this precedence in local app flows: **TEST > LOCAL**
|
||||
|
||||
```python
|
||||
def resolve_runtime_mode(is_test_env: bool) -> RuntimeMode:
|
||||
if is_test_env:
|
||||
return RuntimeMode.TEST
|
||||
return RuntimeMode.LOCAL
|
||||
```
|
||||
|
||||
**Note**: `RuntimeMode` determines global behavior (e.g., whether to start file sync).
|
||||
Per-project routing is orthogonal: individual projects can be set to `cloud` mode via `ProjectMode`,
|
||||
which affects client routing in `get_client(project_name=...)` without changing global runtime mode.
|
||||
`RuntimeMode.CLOUD` may remain for compatibility, but standard local runtime resolution does not select it.
|
||||
|
||||
## Dependencies Package
|
||||
|
||||
### Structure
|
||||
|
||||
The `deps/` package provides FastAPI dependencies organized by feature:
|
||||
|
||||
```
|
||||
src/basic_memory/deps/
|
||||
├── __init__.py # Re-exports for backwards compatibility
|
||||
├── config.py # Configuration access
|
||||
├── db.py # Database/session management
|
||||
├── projects.py # Project resolution
|
||||
├── repositories.py # Data access layer
|
||||
├── services.py # Business logic layer
|
||||
└── importers.py # Import functionality
|
||||
```
|
||||
|
||||
### Usage in Routers
|
||||
|
||||
```python
|
||||
from basic_memory.deps.services import get_entity_service
|
||||
from basic_memory.deps.projects import get_project_config
|
||||
|
||||
@router.get("/entities/{id}")
|
||||
async def get_entity(
|
||||
id: int,
|
||||
entity_service: EntityService = Depends(get_entity_service),
|
||||
project: ProjectConfig = Depends(get_project_config),
|
||||
):
|
||||
return await entity_service.get(id)
|
||||
```
|
||||
|
||||
### Backwards Compatibility
|
||||
|
||||
The old `deps.py` file still exists as a thin re-export shim:
|
||||
|
||||
```python
|
||||
# deps.py - backwards compatibility shim
|
||||
from basic_memory.deps import *
|
||||
```
|
||||
|
||||
New code should import from specific submodules (`basic_memory.deps.services`) for clarity.
|
||||
|
||||
## MCP Tools Architecture
|
||||
|
||||
### Typed API Clients
|
||||
|
||||
MCP tools communicate with the API through typed clients that encapsulate HTTP paths and response validation:
|
||||
|
||||
```
|
||||
src/basic_memory/mcp/clients/
|
||||
├── __init__.py # Re-exports all clients
|
||||
├── base.py # BaseClient with common logic
|
||||
├── knowledge.py # KnowledgeClient - entity CRUD
|
||||
├── search.py # SearchClient - search operations
|
||||
├── memory.py # MemoryClient - context building
|
||||
├── directory.py # DirectoryClient - directory listing
|
||||
├── resource.py # ResourceClient - resource reading
|
||||
└── project.py # ProjectClient - project management
|
||||
```
|
||||
|
||||
### Client Pattern
|
||||
|
||||
Each client encapsulates API paths and validates responses:
|
||||
|
||||
```python
|
||||
class KnowledgeClient(BaseClient):
|
||||
"""Client for knowledge/entity operations."""
|
||||
|
||||
async def resolve_entity(self, identifier: str) -> int:
|
||||
"""Resolve identifier to entity ID."""
|
||||
response = await call_get(
|
||||
self.http_client,
|
||||
f"{self._base_path}/resolve/{identifier}",
|
||||
)
|
||||
return int(response.text)
|
||||
|
||||
async def get_entity(self, entity_id: int) -> EntityResponse:
|
||||
"""Get entity by ID."""
|
||||
response = await call_get(
|
||||
self.http_client,
|
||||
f"{self._base_path}/entities/{entity_id}",
|
||||
)
|
||||
return EntityResponse.model_validate(response.json())
|
||||
```
|
||||
|
||||
### Tool → Client → API Flow
|
||||
|
||||
```
|
||||
MCP Tool (thin adapter)
|
||||
↓
|
||||
Typed Client (encapsulates paths, validates responses)
|
||||
↓
|
||||
HTTP API (FastAPI router)
|
||||
↓
|
||||
Service Layer (business logic)
|
||||
↓
|
||||
Repository Layer (data access)
|
||||
```
|
||||
|
||||
Example tool using typed client:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
async def search_notes(
|
||||
query: str,
|
||||
project: str | None = None,
|
||||
metadata_filters: dict | None = None,
|
||||
tags: list[str] | None = None,
|
||||
status: str | None = None,
|
||||
) -> SearchResponse:
|
||||
async with get_project_client(project, context) as (client, active_project):
|
||||
# Import client inside function to avoid circular imports
|
||||
from basic_memory.mcp.clients import SearchClient
|
||||
from basic_memory.schemas.search import SearchQuery
|
||||
|
||||
search_query = SearchQuery(
|
||||
text=query,
|
||||
metadata_filters=metadata_filters,
|
||||
tags=tags,
|
||||
status=status,
|
||||
)
|
||||
search_client = SearchClient(client, active_project.external_id)
|
||||
return await search_client.search(search_query.model_dump())
|
||||
```
|
||||
|
||||
### Per-Project Client Routing
|
||||
|
||||
`get_project_client()` from `mcp/project_context.py` is an async context manager that:
|
||||
1. Resolves the project name from config (no network call)
|
||||
2. Creates the correctly-routed client based on the project's mode (local ASGI or cloud HTTP with API key)
|
||||
3. Validates the project via the API
|
||||
4. Yields `(client, active_project)` tuple
|
||||
|
||||
This solves the bootstrap problem: you need the project name to choose the right client (local vs cloud), but you need the client to validate the project exists.
|
||||
|
||||
```python
|
||||
from basic_memory.mcp.project_context import get_project_client
|
||||
|
||||
async with get_project_client(project, context) as (client, active_project):
|
||||
# client is routed based on project's mode (local or cloud)
|
||||
# active_project is validated via the API
|
||||
...
|
||||
```
|
||||
|
||||
## Sync Coordination
|
||||
|
||||
### SyncCoordinator
|
||||
|
||||
The `SyncCoordinator` centralizes sync/watch lifecycle management:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class SyncCoordinator:
|
||||
"""Coordinates file sync and watch operations."""
|
||||
|
||||
status: SyncStatus = SyncStatus.NOT_STARTED
|
||||
sync_task: asyncio.Task | None = None
|
||||
watch_service: WatchService | None = None
|
||||
|
||||
async def start(self, ...):
|
||||
"""Start sync and watch operations."""
|
||||
|
||||
async def stop(self):
|
||||
"""Stop all sync operations gracefully."""
|
||||
|
||||
def get_status_info(self) -> dict:
|
||||
"""Get current sync status for observability."""
|
||||
```
|
||||
|
||||
### Status Enum
|
||||
|
||||
```python
|
||||
class SyncStatus(Enum):
|
||||
NOT_STARTED = "not_started"
|
||||
STARTING = "starting"
|
||||
RUNNING = "running"
|
||||
STOPPING = "stopping"
|
||||
STOPPED = "stopped"
|
||||
ERROR = "error"
|
||||
```
|
||||
|
||||
## Project Resolution
|
||||
|
||||
### ProjectResolver
|
||||
|
||||
Unified project selection across all entrypoints:
|
||||
|
||||
```python
|
||||
class ProjectResolver:
|
||||
"""Resolves which project to use based on context."""
|
||||
|
||||
def resolve(
|
||||
self,
|
||||
explicit_project: str | None = None,
|
||||
) -> ResolvedProject:
|
||||
"""Resolve project using three-tier hierarchy:
|
||||
1. Explicit project parameter
|
||||
2. Default project from config
|
||||
3. Single available project
|
||||
"""
|
||||
```
|
||||
|
||||
### Resolution Modes
|
||||
|
||||
```python
|
||||
class ResolutionMode(Enum):
|
||||
EXPLICIT = "explicit" # User specified project
|
||||
DEFAULT = "default" # Using configured default
|
||||
SINGLE_PROJECT = "single" # Only one project exists
|
||||
FALLBACK = "fallback" # Using first available
|
||||
```
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### Container Testing
|
||||
|
||||
Each container has corresponding tests:
|
||||
|
||||
```
|
||||
tests/
|
||||
├── api/test_api_container.py
|
||||
├── mcp/test_mcp_container.py
|
||||
└── cli/test_cli_container.py
|
||||
```
|
||||
|
||||
Tests verify:
|
||||
- Container creation from config
|
||||
- Runtime mode properties
|
||||
- Container accessor functions (get/set)
|
||||
|
||||
### Mocking Typed Clients
|
||||
|
||||
When testing MCP tools, mock at the client level:
|
||||
|
||||
```python
|
||||
def test_search_notes(monkeypatch):
|
||||
import basic_memory.mcp.clients as clients_mod
|
||||
|
||||
class MockSearchClient:
|
||||
async def search(self, query):
|
||||
return SearchResponse(results=[...])
|
||||
|
||||
monkeypatch.setattr(clients_mod, "SearchClient", MockSearchClient)
|
||||
```
|
||||
|
||||
## Design Principles
|
||||
|
||||
### 1. Explicit Dependencies
|
||||
|
||||
Modules receive configuration explicitly rather than reading globals:
|
||||
|
||||
```python
|
||||
# Good - explicit injection
|
||||
async def sync_files(config: BasicMemoryConfig):
|
||||
...
|
||||
|
||||
# Avoid - hidden global access
|
||||
async def sync_files():
|
||||
config = ConfigManager().config # Hidden coupling
|
||||
```
|
||||
|
||||
### 2. Single Responsibility
|
||||
|
||||
Each layer has a clear responsibility:
|
||||
- **Containers**: Wire dependencies
|
||||
- **Clients**: Encapsulate HTTP communication
|
||||
- **Services**: Business logic
|
||||
- **Repositories**: Data access
|
||||
- **Tools/Routers**: Thin adapters
|
||||
|
||||
### 3. Deferred Imports
|
||||
|
||||
To avoid circular imports, typed clients are imported inside functions:
|
||||
|
||||
```python
|
||||
async def my_tool():
|
||||
async with get_client() as client:
|
||||
# Import here to avoid circular dependency
|
||||
from basic_memory.mcp.clients import KnowledgeClient
|
||||
|
||||
knowledge_client = KnowledgeClient(client, project_id)
|
||||
```
|
||||
|
||||
### 4. Backwards Compatibility
|
||||
|
||||
When refactoring, maintain backwards compatibility via shims:
|
||||
|
||||
```python
|
||||
# Old module becomes a shim
|
||||
from basic_memory.new_location import *
|
||||
|
||||
# Docstring explains migration path
|
||||
"""
|
||||
DEPRECATED: Import from basic_memory.new_location instead.
|
||||
This shim will be removed in a future version.
|
||||
"""
|
||||
```
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
src/basic_memory/
|
||||
├── api/
|
||||
│ ├── container.py # API composition root
|
||||
│ ├── routers/ # FastAPI routers
|
||||
│ └── ...
|
||||
├── mcp/
|
||||
│ ├── container.py # MCP composition root
|
||||
│ ├── clients/ # Typed API clients
|
||||
│ ├── tools/ # MCP tool definitions
|
||||
│ └── server.py # MCP server setup
|
||||
├── cli/
|
||||
│ ├── container.py # CLI composition root
|
||||
│ ├── app.py # Typer app
|
||||
│ └── commands/ # CLI command groups
|
||||
├── deps/
|
||||
│ ├── config.py # Config dependencies
|
||||
│ ├── db.py # Database dependencies
|
||||
│ ├── projects.py # Project dependencies
|
||||
│ ├── repositories.py # Repository dependencies
|
||||
│ ├── services.py # Service dependencies
|
||||
│ └── importers.py # Importer dependencies
|
||||
├── sync/
|
||||
│ ├── coordinator.py # SyncCoordinator
|
||||
│ └── ...
|
||||
├── runtime.py # RuntimeMode resolution
|
||||
├── project_resolver.py # Unified project selection
|
||||
└── config.py # Configuration management
|
||||
```
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -1,260 +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 install basic-memory
|
||||
|
||||
# Or with pip
|
||||
pip install basic-memory
|
||||
```
|
||||
|
||||
> **Important**: You need to install Basic Memory using one of the commands above to use the command line tools.
|
||||
|
||||
### 2. Configure Claude Desktop
|
||||
|
||||
Claude Desktop often has trouble finding executables in your user path. Follow these steps for a reliable setup:
|
||||
|
||||
#### Step 1: Find the absolute path to uvx
|
||||
|
||||
Open Terminal and run:
|
||||
```bash
|
||||
which uvx
|
||||
```
|
||||
This will show you the full path (e.g., `/Users/yourusername/.cargo/bin/uvx`).
|
||||
|
||||
#### Step 2: Edit Claude Desktop Configuration
|
||||
|
||||
Edit the configuration file located at `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"basic-memory": {
|
||||
"command": "/absolute/path/to/uvx",
|
||||
"args": [
|
||||
"basic-memory",
|
||||
"mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace `/absolute/path/to/uvx` with the actual path you found in Step 1.
|
||||
|
||||
> **Note**: Using absolute paths is necessary because Claude Desktop cannot access binaries in your user PATH.
|
||||
|
||||
#### Step 3: Restart Claude Desktop
|
||||
|
||||
Close and reopen Claude Desktop for the changes to take effect.
|
||||
|
||||
### 3. Start the Sync Service
|
||||
|
||||
Start the sync service to monitor your files for changes:
|
||||
|
||||
```bash
|
||||
# One-time sync
|
||||
basic-memory sync
|
||||
|
||||
# For continuous monitoring (recommended)
|
||||
basic-memory sync --watch
|
||||
```
|
||||
|
||||
The `--watch` flag enables automatic detection of file changes, keeping your knowledge base current.
|
||||
|
||||
### 4. Staying Updated
|
||||
|
||||
To update Basic Memory when new versions are released:
|
||||
|
||||
```bash
|
||||
# Update with uv (recommended)
|
||||
uv tool upgrade basic-memory
|
||||
|
||||
# Or with pip
|
||||
pip install --upgrade basic-memory
|
||||
```
|
||||
|
||||
> **Note**: After updating, you'll need to restart Claude Desktop and your sync process for changes to take effect.
|
||||
|
||||
## Troubleshooting Installation
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Claude Says "No Basic Memory Tools Available"
|
||||
|
||||
If Claude cannot find Basic Memory tools:
|
||||
|
||||
1. **Check absolute paths**: Ensure you're using complete absolute paths to uvx in the Claude Desktop configuration
|
||||
2. **Verify installation**: Run `basic-memory --version` in Terminal to confirm Basic Memory is installed
|
||||
3. **Restart applications**: Restart both Terminal and Claude Desktop after making configuration changes
|
||||
4. **Check sync status**: Ensure `basic-memory sync --watch` is running
|
||||
|
||||
#### Permission Issues
|
||||
|
||||
If you encounter permission errors:
|
||||
1. Check that Basic Memory has access to create files in your home directory
|
||||
2. Ensure Claude Desktop has permission to execute the uvx command
|
||||
|
||||
## Creating Your First Knowledge Note
|
||||
|
||||
1. **Start the sync process** in a Terminal window:
|
||||
```bash
|
||||
basic-memory sync --watch
|
||||
```
|
||||
Keep this running in the background.
|
||||
|
||||
2. **Open Claude Desktop** and start a new conversation.
|
||||
|
||||
3. **Have a natural conversation** about any topic:
|
||||
```
|
||||
You: "Let's talk about coffee brewing methods I've been experimenting with."
|
||||
Claude: "I'd be happy to discuss coffee brewing methods..."
|
||||
You: "I've found that pour over gives more flavor clarity than French press..."
|
||||
```
|
||||
|
||||
4. **Ask Claude to create a note**:
|
||||
```
|
||||
You: "Could you create a note summarizing what we've discussed about coffee brewing?"
|
||||
```
|
||||
|
||||
5. **Confirm note creation**:
|
||||
Claude will confirm when the note has been created and where it's stored.
|
||||
|
||||
6. **View the created file** in your `~/basic-memory` directory using any text editor or Obsidian.
|
||||
The file structure will look similar to:
|
||||
```markdown
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
permalink: coffee-brewing-methods
|
||||
---
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
## Observations
|
||||
- [method] Pour over provides more clarity...
|
||||
- [technique] Water temperature at 205°F...
|
||||
|
||||
## Relations
|
||||
- relates_to [[Other Coffee Topics]]
|
||||
```
|
||||
|
||||
## Using Special Prompts
|
||||
|
||||
Basic Memory includes special prompts that help you start conversations with context from your knowledge base:
|
||||
|
||||
### Continue Conversation
|
||||
|
||||
To resume a previous topic:
|
||||
|
||||
```
|
||||
You: "Let's continue our conversation about coffee brewing."
|
||||
```
|
||||
|
||||
This prompt triggers Claude to:
|
||||
|
||||
1. Search your knowledge base for relevant content about coffee brewing
|
||||
2. Build context from these documents
|
||||
3. Resume the conversation with full awareness of previous discussions
|
||||
|
||||
### Recent Activity
|
||||
|
||||
To see what you've been working on:
|
||||
|
||||
```
|
||||
You: "What have we been discussing recently?"
|
||||
```
|
||||
|
||||
This prompt causes Claude to:
|
||||
|
||||
1. Retrieve documents modified in the recent past
|
||||
2. Summarize the topics and main points
|
||||
3. Offer to continue any of those discussions
|
||||
|
||||
### Search
|
||||
|
||||
To find specific information:
|
||||
|
||||
```
|
||||
You: "Find information about pour over coffee methods."
|
||||
```
|
||||
|
||||
Claude will:
|
||||
|
||||
1. Search your knowledge base for relevant documents
|
||||
2. Summarize the key findings
|
||||
3. Offer to explore specific documents in more detail
|
||||
|
||||
See [[User Guide#Using Special Prompts]] for further information.
|
||||
|
||||
## Using Your Knowledge Base
|
||||
|
||||
### Referencing Knowledge
|
||||
|
||||
In future conversations, reference your existing knowledge:
|
||||
|
||||
```
|
||||
You: "What water temperature did we decide was optimal for coffee brewing?"
|
||||
```
|
||||
|
||||
Or directly reference notes using memory:// URLs:
|
||||
|
||||
```
|
||||
You: "Take a look at memory://coffee-brewing-methods and let's discuss how to improve my technique."
|
||||
```
|
||||
|
||||
### Building On Previous Knowledge
|
||||
|
||||
Basic Memory enables continuous knowledge building:
|
||||
|
||||
1. **Reference previous discussions** in new conversations
|
||||
2. **Add to existing notes** through conversations
|
||||
3. **Create connections** between related topics
|
||||
4. **Follow relationships** to build comprehensive context
|
||||
|
||||
## Importing Existing Conversations
|
||||
|
||||
Import your existing AI conversations:
|
||||
|
||||
```bash
|
||||
# From Claude
|
||||
basic-memory import claude conversations
|
||||
|
||||
# From ChatGPT
|
||||
basic-memory import chatgpt
|
||||
```
|
||||
|
||||
After importing, run `basic-memory sync` to index everything.
|
||||
|
||||
## Quick Tips
|
||||
|
||||
- Keep `basic-memory sync --watch` running in a terminal window
|
||||
- Use special prompts (Continue Conversation, Recent Activity, Search) to start contextual discussions
|
||||
- Build connections between notes for a richer knowledge graph
|
||||
- Use direct memory:// URLs when you need precise context
|
||||
- Use git to version control your knowledge base
|
||||
- Review and edit AI-generated notes for accuracy
|
||||
|
||||
## Next Steps
|
||||
|
||||
After getting started, explore these areas:
|
||||
|
||||
1. **Read the [[User Guide]]** for comprehensive usage instructions
|
||||
2. **Understand the [[Knowledge Format]]** to learn how knowledge is structured
|
||||
3. **Set up [[Obsidian Integration]]** for visual knowledge navigation
|
||||
4. **Learn about [[Canvas]]** visualizations for mapping concepts
|
||||
5. **Review the [[CLI Reference]]** for command line tools
|
||||
@@ -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)
|
||||
@@ -0,0 +1,494 @@
|
||||
# Note Format Reference
|
||||
|
||||
Every document in Basic Memory is a plain Markdown file. Files are the source of truth — changes to files automatically update the knowledge graph in the database. You maintain complete ownership, files work with git, and knowledge persists independently of any AI conversation.
|
||||
|
||||
## Document Structure
|
||||
|
||||
A note has three parts: YAML frontmatter, content (observations), and relations.
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
type: note
|
||||
tags: [coffee, brewing]
|
||||
permalink: coffee-brewing-methods
|
||||
---
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
## Observations
|
||||
- [method] Pour over provides more flavor clarity than French press
|
||||
- [technique] Water temperature at 205°F extracts optimal compounds #brewing
|
||||
- [preference] Ethiopian beans work well with lighter roasts (personal experience)
|
||||
|
||||
## Relations
|
||||
- relates_to [[Coffee Bean Origins]]
|
||||
- requires [[Proper Grinding Technique]]
|
||||
- contrasts_with [[Tea Brewing Methods]]
|
||||
```
|
||||
|
||||
The `## Observations` and `## Relations` headings are conventional but not required — the parser detects observations and relations by their syntax patterns anywhere in the document.
|
||||
|
||||
## Frontmatter
|
||||
|
||||
YAML metadata between `---` fences at the top of the file.
|
||||
|
||||
| Field | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| `title` | No | filename stem | Used for linking and references. Auto-set from filename if missing. |
|
||||
| `type` | No | `note` | Entity type. Used for schema resolution and filtering. |
|
||||
| `tags` | No | `[]` | List or comma-separated string. Used for organization and search. |
|
||||
| `permalink` | No | generated from title | Stable identifier. Persists even if the file moves. |
|
||||
| `schema` | No | none | Schema attachment — dict (inline), string (reference), or omitted (implicit). |
|
||||
|
||||
Custom fields are allowed. Any key not in the standard set is stored as `entity_metadata` and indexed for search and filtering.
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Paul Graham
|
||||
type: Person
|
||||
tags: [startups, essays, lisp]
|
||||
permalink: paul-graham
|
||||
status: active
|
||||
source: wikipedia
|
||||
---
|
||||
```
|
||||
|
||||
Here `status` and `source` are custom fields stored in `entity_metadata`.
|
||||
|
||||
### Frontmatter Value Handling
|
||||
|
||||
YAML automatically converts some values to native types. Basic Memory normalizes them:
|
||||
|
||||
- Date strings (`2025-10-24`) → kept as ISO format strings
|
||||
- Numbers (`1.0`) → converted to strings
|
||||
- Booleans (`true`) → converted to strings (`"True"`)
|
||||
- Lists and dicts → preserved, items normalized recursively
|
||||
|
||||
This prevents errors when downstream code expects string values.
|
||||
|
||||
## Observations
|
||||
|
||||
An observation is a categorized fact about the entity. Written as a Markdown list item.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
```
|
||||
- [category] content text #tag1 #tag2 (context)
|
||||
```
|
||||
|
||||
| Part | Required | Description |
|
||||
|------|----------|-------------|
|
||||
| `[category]` | Yes | Classification in square brackets. Any text except `[]()` chars. |
|
||||
| content | Yes | The fact or statement. |
|
||||
| `#tags` | No | Inline tags. Space-separated, each starting with `#`. |
|
||||
| `(context)` | No | Parenthesized text at end of line. Supporting details or source. |
|
||||
|
||||
### Examples
|
||||
|
||||
```markdown
|
||||
- [tech] Uses SQLite for storage #database
|
||||
- [design] Follows local-first architecture #architecture
|
||||
- [decision] Selected bcrypt for passwords #security (based on OWASP audit)
|
||||
- [name] Paul Graham
|
||||
- [expertise] Startups
|
||||
- [expertise] Lisp
|
||||
- [expertise] Essay writing
|
||||
```
|
||||
|
||||
Array-like fields use repeated categories — multiple `[expertise]` observations above.
|
||||
|
||||
### What Is Not an Observation
|
||||
|
||||
The parser excludes these list item patterns:
|
||||
|
||||
| Pattern | Example | Reason |
|
||||
|---------|---------|--------|
|
||||
| Checkboxes | `- [ ] Todo item`, `- [x] Done`, `- [-] Cancelled` | Task list syntax |
|
||||
| Markdown links | `- [text](url)` | URL link syntax |
|
||||
| Bare wiki links | `- [[Target]]` | Treated as a relation instead |
|
||||
|
||||
A list item with `#tags` but no `[category]` is still parsed — the tags are extracted and the category defaults to `Note`.
|
||||
|
||||
## Relations
|
||||
|
||||
Relations connect documents to form the knowledge graph. There are two kinds.
|
||||
|
||||
### Explicit Relations
|
||||
|
||||
Written as list items with a relation type and a `[[wiki link]]` target.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
```
|
||||
- relation_type [[Target Entity]] (context)
|
||||
```
|
||||
|
||||
| Part | Required | Description |
|
||||
|------|----------|-------------|
|
||||
| `relation_type` | No | Text before `[[`. Defaults to `relates_to` if omitted. |
|
||||
| `[[Target]]` | Yes | Wiki link to the target entity. Matched by title or permalink. |
|
||||
| `(context)` | No | Parenthesized text after `]]`. Supporting details. |
|
||||
|
||||
### Examples
|
||||
|
||||
```markdown
|
||||
- implements [[Search Design]]
|
||||
- depends_on [[Database Schema]]
|
||||
- works_at [[Y Combinator]] (co-founder)
|
||||
- [[Some Entity]]
|
||||
```
|
||||
|
||||
The last example — a bare `[[wiki link]]` in a list item — gets relation type `relates_to`.
|
||||
|
||||
Common relation types:
|
||||
- `implements`, `depends_on`, `relates_to`, `inspired_by`
|
||||
- `extends`, `part_of`, `contains`, `pairs_with`
|
||||
- `works_at`, `authored`, `collaborated_with`
|
||||
|
||||
Any text works as a relation type. These are conventions, not a fixed set.
|
||||
|
||||
### Inline References
|
||||
|
||||
Wiki links appearing in regular prose (not as list items) create implicit `links_to` relations.
|
||||
|
||||
```markdown
|
||||
This builds on [[Core Design]] and uses [[Utility Functions]].
|
||||
```
|
||||
|
||||
This creates two relations: `links_to [[Core Design]]` and `links_to [[Utility Functions]]`.
|
||||
|
||||
### Forward References
|
||||
|
||||
Relations can link to entities that don't exist yet. Basic Memory resolves them when the target is created.
|
||||
|
||||
## Permalinks and memory:// URLs
|
||||
|
||||
Every document has a unique **permalink** — a stable identifier derived from its title. You can set one explicitly in frontmatter, or let the system generate it.
|
||||
|
||||
```yaml
|
||||
permalink: auth-approaches-2024
|
||||
```
|
||||
|
||||
Permalinks form the basis of `memory://` URLs:
|
||||
|
||||
```
|
||||
memory://auth-approaches-2024 # By permalink
|
||||
memory://Authentication Approaches # By title (auto-resolves)
|
||||
memory://project/auth-approaches # By path
|
||||
```
|
||||
|
||||
Pattern matching is supported:
|
||||
|
||||
```
|
||||
memory://auth* # Starts with "auth"
|
||||
memory://*/approaches # Ends with "approaches"
|
||||
memory://project/*/requirements # Nested wildcard
|
||||
```
|
||||
|
||||
## Schemas
|
||||
|
||||
Schemas declare the expected structure of a note — which observation categories and relation types a well-formed note should have. They use Picoschema, a compact notation from Google's Dotprompt that fits naturally in YAML frontmatter.
|
||||
|
||||
### Picoschema Syntax
|
||||
|
||||
```yaml
|
||||
schema:
|
||||
name: string, full name # required field with description
|
||||
email?: string, contact email # ? = optional
|
||||
role?: string, job title
|
||||
works_at?: Organization, employer # capitalized type = entity reference
|
||||
tags?(array): string, categories # array of type
|
||||
status?(enum): [active, inactive] # enum with allowed values
|
||||
metadata?(object): # nested object
|
||||
updated_at?: string
|
||||
source?: string
|
||||
```
|
||||
|
||||
| Notation | Meaning | Example |
|
||||
|----------|---------|---------|
|
||||
| `field: type` | Required field | `name: string` |
|
||||
| `field?: type` | Optional field | `role?: string` |
|
||||
| `field(array): type` | Array of values | `expertise(array): string` |
|
||||
| `field?(enum): [vals]` | Enum with allowed values | `status?(enum): [active, inactive]` |
|
||||
| `field?(object):` | Nested object with sub-fields | `metadata?(object):` |
|
||||
| `, description` | Description after comma | `name: string, full name` |
|
||||
| `EntityName` | Capitalized type = entity reference | `works_at?: Organization` |
|
||||
|
||||
**Scalar types:** `string`, `integer`, `number`, `boolean`, `any`
|
||||
|
||||
Any type not in that set whose first letter is uppercase is treated as an entity reference (a relation target).
|
||||
|
||||
### Schema-to-Note Mapping
|
||||
|
||||
Schemas validate against existing observation/relation syntax. Note authors don't learn new syntax.
|
||||
|
||||
| Schema Declaration | Maps To | Example in Note |
|
||||
|--------------------|---------|-----------------|
|
||||
| `field: string` | Observation `[field] value` | `- [name] Paul Graham` |
|
||||
| `field?(array): string` | Multiple `[field]` observations | `- [expertise] Lisp` (repeated) |
|
||||
| `field?: EntityType` | Relation `field [[Target]]` | `- works_at [[Y Combinator]]` |
|
||||
| `field?(array): EntityType` | Multiple `field` relations | `- authored [[Book]]` (repeated) |
|
||||
| `tags` | Frontmatter `tags` array | `tags: [startups, essays]` |
|
||||
| `field?(enum): [vals]` | Observation `[field] value` where value is in the set | `- [status] active` |
|
||||
|
||||
Observations and relations not covered by the schema are valid — schemas describe a subset, not a straitjacket.
|
||||
|
||||
### Schema Attachment
|
||||
|
||||
Three ways to attach a schema to a note, resolved in priority order:
|
||||
|
||||
**1. Inline schema** — `schema` is a dict in frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Team Standup 2024-01-15
|
||||
type: meeting
|
||||
schema:
|
||||
attendees(array): string, who was there
|
||||
decisions(array): string, what was decided
|
||||
action_items(array): string, follow-ups
|
||||
blockers?(array): string, anything stuck
|
||||
---
|
||||
```
|
||||
|
||||
Good for one-off structured notes or prototyping a schema before extracting it.
|
||||
|
||||
**2. Explicit reference** — `schema` is a string naming a schema note:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Basic Memory
|
||||
schema: SoftwareProject
|
||||
---
|
||||
```
|
||||
|
||||
or by permalink:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: LLM Memory Patterns
|
||||
schema: schema/research-project
|
||||
---
|
||||
```
|
||||
|
||||
Use when the note's `type` differs from the schema it should validate against, or when multiple schema variants exist.
|
||||
|
||||
**3. Implicit by type** — no `schema` field, resolved by matching `type`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Paul Graham
|
||||
type: Person
|
||||
---
|
||||
```
|
||||
|
||||
The system looks up a schema note where `entity: Person`. If found, it applies. If not, no validation occurs.
|
||||
|
||||
**4. No schema** — perfectly fine. Most notes don't need one.
|
||||
|
||||
### Schema Notes
|
||||
|
||||
A schema is itself a Basic Memory note with `type: schema`. It lives anywhere (though `schema/` is the conventional directory).
|
||||
|
||||
```yaml
|
||||
# schema/Person.md
|
||||
---
|
||||
title: Person
|
||||
type: schema
|
||||
entity: Person
|
||||
version: 1
|
||||
schema:
|
||||
name: string, full name
|
||||
role?: string, job title or position
|
||||
works_at?: Organization, employer
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
settings:
|
||||
validation: warn
|
||||
---
|
||||
|
||||
# Person
|
||||
|
||||
A human individual in the knowledge graph.
|
||||
```
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `type` | Yes | Must be `schema` |
|
||||
| `entity` | Yes | The entity type this schema describes (e.g., `Person`) |
|
||||
| `version` | No | Schema version number (default: `1`) |
|
||||
| `schema` | Yes | Picoschema dict defining the fields |
|
||||
| `settings.validation` | No | Validation mode (default: `warn`) |
|
||||
|
||||
Schema notes are regular notes — they show up in search, can have observations and relations, and participate in the knowledge graph.
|
||||
|
||||
### Validation Modes
|
||||
|
||||
| Mode | Behavior |
|
||||
|------|----------|
|
||||
| `warn` | Warnings in output, doesn't block (default) |
|
||||
| `strict` | Errors that block sync, for CI/CD enforcement |
|
||||
| `off` | No validation |
|
||||
|
||||
### Validation Output
|
||||
|
||||
```
|
||||
$ bm schema validate people/ada-lovelace.md
|
||||
|
||||
⚠ Person schema validation:
|
||||
- Missing required field: name (expected [name] observation)
|
||||
- Missing optional field: role
|
||||
- Missing optional field: works_at (no relation found)
|
||||
|
||||
ℹ Unmatched observations: [fact] ×2, [born] ×1
|
||||
ℹ Unmatched relations: collaborated_with
|
||||
```
|
||||
|
||||
"Unmatched" items are informational — observations and relations the schema doesn't cover.
|
||||
|
||||
### Schema Inference
|
||||
|
||||
Generate schemas from existing notes by analyzing observation and relation frequency:
|
||||
|
||||
```
|
||||
$ bm schema infer Person
|
||||
|
||||
Analyzing 30 notes with type: Person...
|
||||
|
||||
Observations found:
|
||||
[name] 30/30 100% → name: string
|
||||
[role] 27/30 90% → role?: string
|
||||
[expertise] 18/30 60% → expertise?(array): string
|
||||
[email] 8/30 27% → email?: string
|
||||
|
||||
Relations found:
|
||||
works_at 22/30 73% → works_at?: Organization
|
||||
|
||||
Suggested schema:
|
||||
name: string, full name
|
||||
role?: string, job title
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
works_at?: Organization, employer
|
||||
|
||||
Save to schema/Person.md? [y/n]
|
||||
```
|
||||
|
||||
Frequency thresholds:
|
||||
- **100% present** → required field
|
||||
- **25%+ present** → optional field
|
||||
- **Below 25%** → excluded from suggestion
|
||||
|
||||
### Schema Drift Detection
|
||||
|
||||
Track how usage patterns shift over time:
|
||||
|
||||
```
|
||||
$ bm schema diff Person
|
||||
|
||||
Schema drift detected:
|
||||
|
||||
+ expertise: now in 81% of notes (was 12%)
|
||||
- department: dropped to 3% of notes
|
||||
~ works_at: cardinality changed (one → many)
|
||||
|
||||
Update schema? [y/n/review]
|
||||
```
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Simple Note (No Schema)
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Project Ideas
|
||||
type: note
|
||||
tags: [ideas, brainstorm]
|
||||
---
|
||||
|
||||
# Project Ideas
|
||||
|
||||
## Observations
|
||||
- [idea] Build a CLI tool for markdown linting #tooling
|
||||
- [idea] Create a recipe knowledge base #cooking
|
||||
- [priority] Focus on developer tools first (Q1 goal)
|
||||
|
||||
## Relations
|
||||
- inspired_by [[Developer Workflow Research]]
|
||||
- part_of [[Q1 Planning]]
|
||||
```
|
||||
|
||||
### Schema-Validated Note
|
||||
|
||||
Schema at `schema/Person.md`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Person
|
||||
type: schema
|
||||
entity: Person
|
||||
version: 1
|
||||
schema:
|
||||
name: string, full name
|
||||
role?: string, job title or position
|
||||
works_at?: Organization, employer
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
settings:
|
||||
validation: warn
|
||||
---
|
||||
|
||||
# Person
|
||||
|
||||
A human individual in the knowledge graph.
|
||||
```
|
||||
|
||||
Note at `people/paul-graham.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Paul Graham
|
||||
type: Person
|
||||
tags: [startups, essays, lisp]
|
||||
---
|
||||
|
||||
# Paul Graham
|
||||
|
||||
## Observations
|
||||
- [name] Paul Graham
|
||||
- [role] Essayist and investor
|
||||
- [expertise] Startups
|
||||
- [expertise] Lisp
|
||||
- [expertise] Essay writing
|
||||
- [fact] Created Viaweb, the first web app
|
||||
|
||||
## Relations
|
||||
- works_at [[Y Combinator]]
|
||||
- authored [[Hackers and Painters]]
|
||||
```
|
||||
|
||||
The `[fact]` observation and `authored` relation are not in the schema — they're valid, just unmatched. The schema only checks that `[name]` exists (required) and looks for optional fields like `[role]`, `[expertise]`, and `works_at`.
|
||||
|
||||
### Inline Schema Note
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Team Standup 2024-01-15
|
||||
type: meeting
|
||||
schema:
|
||||
attendees(array): string, who was there
|
||||
decisions(array): string, what was decided
|
||||
action_items(array): string, follow-ups
|
||||
blockers?(array): string, anything stuck
|
||||
---
|
||||
|
||||
# Team Standup 2024-01-15
|
||||
|
||||
## Observations
|
||||
- [attendees] Paul
|
||||
- [attendees] Sarah
|
||||
- [decisions] Ship v2 by Friday
|
||||
- [action_items] Paul to review PR #42
|
||||
- [blockers] Waiting on API credentials
|
||||
```
|
||||
@@ -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)
|
||||
@@ -0,0 +1,147 @@
|
||||
# Simplified Local/Cloud Routing
|
||||
|
||||
## Context
|
||||
|
||||
Basic Memory now uses explicit, project-aware routing without a global cloud-mode toggle.
|
||||
Routing is determined by command-level flags and project mode, not by a global `cloud_mode` state.
|
||||
|
||||
This document is the canonical contract for local/cloud routing behavior in CLI, MCP, and API-adjacent clients.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Remove global `cloud_mode` from runtime/routing semantics.
|
||||
2. Keep MCP HTTP/SSE local-only; let stdio honor per-project routing.
|
||||
3. Make CLI routing explicit and easy to reason about.
|
||||
4. Support projects that exist in both local and cloud without ambiguity.
|
||||
|
||||
## Routing Contract
|
||||
|
||||
Routing is resolved in this order:
|
||||
|
||||
1. Injected client factory (for composition/integration contexts)
|
||||
2. Explicit routing override (`--local` / `--cloud` or env vars below)
|
||||
3. Project-scoped routing (`project.mode`) when a project is known
|
||||
4. Default local routing
|
||||
|
||||
### Routing Environment Variables
|
||||
|
||||
- `BASIC_MEMORY_FORCE_LOCAL=true`: force local transport
|
||||
- `BASIC_MEMORY_FORCE_CLOUD=true`: force cloud proxy transport
|
||||
- `BASIC_MEMORY_EXPLICIT_ROUTING=true`: marks routing as explicitly chosen for this command
|
||||
|
||||
When explicit routing is active, project mode does not override the selected route.
|
||||
|
||||
## Config Semantics
|
||||
|
||||
- `project.mode` is the only config-based routing signal for project-scoped operations.
|
||||
- Legacy `cloud_mode` values may be encountered during migration/loading but are not used for routing behavior.
|
||||
- Normalization saves remove stale `cloud_mode` from `~/.basic-memory/config.json`.
|
||||
|
||||
### Example Config
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": {
|
||||
"main": {
|
||||
"path": "/Users/me/basic-memory",
|
||||
"mode": "local",
|
||||
"local_sync_path": null,
|
||||
"bisync_initialized": false,
|
||||
"last_sync": null
|
||||
},
|
||||
"specs": {
|
||||
"path": "specs",
|
||||
"mode": "cloud",
|
||||
"local_sync_path": "/Users/me/dev/specs",
|
||||
"bisync_initialized": true,
|
||||
"last_sync": "2026-02-06T17:36:38.544153"
|
||||
}
|
||||
},
|
||||
"default_project": "main",
|
||||
"cloud_api_key": "bmc_abc123...",
|
||||
"cloud_host": "https://cloud.basicmemory.com"
|
||||
}
|
||||
```
|
||||
|
||||
## Cloud Commands Are Auth-Only
|
||||
|
||||
`bm cloud login`, `bm cloud logout`, and `bm cloud status` manage authentication state.
|
||||
|
||||
- `bm cloud login`
|
||||
- performs OAuth device flow
|
||||
- stores/refreshes token material
|
||||
- may verify cloud health/subscription
|
||||
- does not change routing defaults
|
||||
- `bm cloud logout`
|
||||
- removes stored OAuth session tokens
|
||||
- does not change routing defaults
|
||||
- `bm cloud status`
|
||||
- reports auth state (API key, OAuth token validity)
|
||||
- runs health checks only when credentials are available
|
||||
|
||||
## MCP Transport Routing
|
||||
|
||||
### Stdio (default)
|
||||
|
||||
`bm mcp --transport stdio` uses natural per-project routing.
|
||||
|
||||
- Local-mode projects route through the in-process ASGI transport.
|
||||
- Cloud-mode projects route to the cloud proxy with Bearer auth (API key).
|
||||
- No explicit routing env vars are injected by the CLI command.
|
||||
- Externally-set env vars are honored (e.g. `BASIC_MEMORY_FORCE_CLOUD=true` for cloud deployments).
|
||||
- Users who need all projects forced local can set `BASIC_MEMORY_FORCE_LOCAL=true` externally.
|
||||
|
||||
### HTTP and SSE Transports
|
||||
|
||||
`bm mcp --transport streamable-http` and `bm mcp --transport sse` always route locally.
|
||||
|
||||
These transports set explicit local routing (`BASIC_MEMORY_FORCE_LOCAL=true` and
|
||||
`BASIC_MEMORY_EXPLICIT_ROUTING=true`) before starting the server. This prevents cloud
|
||||
routing regardless of project mode, since HTTP/SSE serve as local API endpoints.
|
||||
|
||||
## Project List UX for Dual Presence
|
||||
|
||||
Projects may exist in both local and cloud. `bm project list` should display that clearly in one row per logical
|
||||
project identity, with explicit source/target signals.
|
||||
|
||||
Recommended display contract:
|
||||
|
||||
1. Keep one row per normalized project name/permalink.
|
||||
2. Show both local and cloud presence as separate columns/indicators.
|
||||
3. Show an explicit `MCP (stdio)` target column that always resolves to `local`.
|
||||
4. Keep CLI route semantics explicit:
|
||||
- no flags: default local for non-project commands
|
||||
- `--cloud`: force cloud
|
||||
- `--local`: force local
|
||||
|
||||
## Project LS Targeting
|
||||
|
||||
`bm project ls` should clearly identify which project instance is being listed.
|
||||
|
||||
Targeting rules:
|
||||
|
||||
1. No routing flags: list local project files.
|
||||
2. `--cloud`: list cloud project files.
|
||||
3. `--local`: list local project files (explicit override).
|
||||
4. Output should label the active target (`LOCAL` or `CLOUD`) in heading or status line.
|
||||
|
||||
## Runtime Mode
|
||||
|
||||
Runtime mode is no longer a cloud/local routing switch for local app flows.
|
||||
|
||||
- `resolve_runtime_mode(is_test_env)` resolves to:
|
||||
- `TEST` when running in test environment
|
||||
- `LOCAL` otherwise
|
||||
- `RuntimeMode.CLOUD` may remain for compatibility with existing tests/call sites but is not selected by normal local
|
||||
runtime resolution.
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
1. Loading config with legacy `cloud_mode` succeeds.
|
||||
2. Saving config strips legacy `cloud_mode`.
|
||||
3. `--local/--cloud` always override per-project mode for that command.
|
||||
4. No-project + no-flags commands route local by default.
|
||||
5. `bm cloud login/logout` do not toggle routing behavior.
|
||||
6. `bm mcp` stdio routes per-project mode; HTTP/SSE remain local-forced.
|
||||
7. `bm project list` communicates dual local/cloud presence without ambiguity.
|
||||
8. `bm project ls` output identifies route target explicitly.
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -1,62 +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.
|
||||
|
||||
![[Obsidian-CoffeeKnowledgeBase-examples-overlays.gif]]
|
||||
|
||||
Basic Memory uses:
|
||||
|
||||
- **Files as the source of truth** - Everything is stored in plain Markdown files
|
||||
- **Git-compatible storage** - All knowledge can be versioned, branched, and merged
|
||||
- **Local SQLite database** - For fast indexing and searching only (not primary storage)
|
||||
- **Model Context Protocol (MCP)** - For seamless AI assistant integration
|
||||
|
||||
Basic Memory gives you complete control over your knowledge:
|
||||
|
||||
- **Local-first storage** - All knowledge lives on your computer
|
||||
- **Standard file formats** - Plain Markdown compatible with any editor
|
||||
- **Directory organization** - Knowledge stored in `~/basic-memory` by default
|
||||
- **Version control ready** - Use git for history, branching, and collaboration
|
||||
- **Edit anywhere** - Modify files with any text editor or Obsidian
|
||||
|
||||
Changes to files automatically sync with the knowledge graph, and AI assistants can see your edits in conversations.
|
||||
|
||||
## Documentation Map
|
||||
|
||||
Continue exploring Basic Memory with these guides:
|
||||
|
||||
- Installation and setup [[Getting Started with Basic Memory]]
|
||||
- Comprehensive usage instructions [[User Guide]]
|
||||
- Detailed explanation of knowledge structure [[Knowledge Format]]
|
||||
- Obsidian integration guide [[Obsidian Integration]]
|
||||
- Canvas visualization guide [[Canvas]]
|
||||
- Command line tool reference [[CLI Reference]]
|
||||
- Reference for AI assistants using Basic Memory [[AI Assistant Guide]]
|
||||
- Technical implementation details [[Technical Information]]
|
||||
|
||||
## Next Steps
|
||||
|
||||
Start with the [[Getting Started with Basic Memory]] guide to install Basic Memory and configure it with your AI
|
||||
assistant.
|
||||
|
Before Width: | Height: | Size: 374 KiB |
|
Before Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 908 KiB |
|
Before Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 302 KiB |
|
Before Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
@@ -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.
|
||||
@@ -0,0 +1,855 @@
|
||||
# Basic Memory Cloud CLI Guide
|
||||
|
||||
The Basic Memory Cloud CLI provides seamless integration between local and cloud knowledge bases using **project-scoped synchronization**. Each project can optionally sync with the cloud, giving you fine-grained control over what syncs and where.
|
||||
|
||||
## Overview
|
||||
|
||||
The cloud CLI enables you to:
|
||||
- **Authenticate cloud access** - OAuth/API key credentials are stored locally for cloud operations
|
||||
- **Project-scoped sync** - Each project independently manages its sync configuration
|
||||
- **Explicit operations** - Sync only what you want, when you want
|
||||
- **Bidirectional sync** - Keep local and cloud in sync with rclone bisync
|
||||
- **Offline access** - Work locally, sync when ready
|
||||
|
||||
## 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
|
||||
- **Optional**: Cloud is optional. Local-first open-source usage continues without cloud.
|
||||
- **OSS Discount**: Use code `{{OSS_DISCOUNT_CODE}}` for 20% off for 3 months.
|
||||
|
||||
If you attempt to log in without an active subscription, you'll receive a "Subscription Required" error with a link to subscribe.
|
||||
|
||||
## Architecture: Project-Scoped Sync
|
||||
|
||||
### The Problem
|
||||
|
||||
**Old approach (SPEC-8):** All projects lived in a single `~/basic-memory-cloud-sync/` directory. This caused:
|
||||
- ❌ Directory conflicts between mount and bisync
|
||||
- ❌ Auto-discovery creating phantom projects
|
||||
- ❌ Confusion about what syncs and when
|
||||
- ❌ All-or-nothing sync (couldn't sync just one project)
|
||||
|
||||
**New approach (SPEC-20):** Each project independently configures sync.
|
||||
|
||||
### How It Works
|
||||
|
||||
**Projects can exist in three states:**
|
||||
|
||||
1. **Cloud-only** - Project exists on cloud, no local copy
|
||||
2. **Cloud + Local (synced)** - Project has a local working directory that syncs
|
||||
3. **Local-only** - Project exists locally and is not routed to cloud
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# You have 3 projects on cloud:
|
||||
# - research: wants local sync at ~/Documents/research
|
||||
# - work: wants local sync at ~/work-notes
|
||||
# - temp: cloud-only, no local sync needed
|
||||
|
||||
bm project add research --cloud --local-path ~/Documents/research
|
||||
bm project add work --cloud --local-path ~/work-notes
|
||||
bm project add temp --cloud # No local sync
|
||||
|
||||
# Now you can sync individually (after initial --resync):
|
||||
bm project bisync --name research
|
||||
bm project bisync --name work
|
||||
# temp stays cloud-only
|
||||
```
|
||||
|
||||
**What happens under the covers:**
|
||||
- Config stores `cloud_projects` dict mapping project names to local paths
|
||||
- Each project gets its own bisync state in `~/.basic-memory/bisync-state/{project}/`
|
||||
- Rclone syncs using single remote: `basic-memory-cloud`
|
||||
- Projects can live anywhere on your filesystem, not forced into sync directory
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Authenticate Cloud Access
|
||||
|
||||
Authenticate with cloud:
|
||||
|
||||
```bash
|
||||
bm cloud login
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
1. Opens browser to Basic Memory Cloud authentication page
|
||||
2. Stores authentication tokens in `~/.basic-memory/basic-memory-cloud.json`
|
||||
3. Validates your subscription status
|
||||
4. Leaves routing behavior unchanged (auth only)
|
||||
|
||||
**Result:** Cloud credentials are available for cloud-routed commands.
|
||||
Apply OSS discount code `{{OSS_DISCOUNT_CODE}}` during checkout to receive 20% off for 3 months.
|
||||
|
||||
### 2. Set Up Sync
|
||||
|
||||
Install rclone and configure credentials:
|
||||
|
||||
```bash
|
||||
bm cloud setup
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
1. Installs rclone automatically (if needed)
|
||||
2. Fetches your tenant information from cloud
|
||||
3. Generates scoped S3 credentials for sync
|
||||
4. Configures single rclone remote: `basic-memory-cloud`
|
||||
|
||||
**Result:** You're ready to sync projects. No sync directories created yet - those come with project setup.
|
||||
|
||||
### 3. Add Projects with Sync
|
||||
|
||||
Create projects with optional local sync paths:
|
||||
|
||||
```bash
|
||||
# Create cloud project without local sync
|
||||
bm project add research --cloud
|
||||
|
||||
# Create cloud project WITH local sync
|
||||
bm project add research --cloud --local-path ~/Documents/research
|
||||
|
||||
# Or configure sync for existing project
|
||||
bm project sync-setup research ~/Documents/research
|
||||
```
|
||||
|
||||
**What happens under the covers:**
|
||||
|
||||
When you add a project with `--local-path`:
|
||||
1. Project created on cloud at `/app/data/research`
|
||||
2. Local path stored in config for that project (`local_sync_path`)
|
||||
3. Local directory created if it doesn't exist
|
||||
4. Bisync state directory created at `~/.basic-memory/bisync-state/research/`
|
||||
|
||||
**Result:** Project is ready to sync, but no files synced yet.
|
||||
|
||||
### 4. Sync Your Project
|
||||
|
||||
Establish the initial sync baseline. **Best practice:** Always preview with `--dry-run` first:
|
||||
|
||||
```bash
|
||||
# Step 1: Preview the initial sync (recommended)
|
||||
bm project bisync --name research --resync --dry-run
|
||||
|
||||
# Step 2: If all looks good, run the actual sync
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**What happens under the covers:**
|
||||
1. Rclone reads from `~/Documents/research` (local)
|
||||
2. Connects to `basic-memory-cloud:bucket-name/app/data/research` (remote)
|
||||
3. Creates bisync state files in `~/.basic-memory/bisync-state/research/`
|
||||
4. Syncs files bidirectionally with settings:
|
||||
- `conflict_resolve=newer` (most recent wins)
|
||||
- `max_delete=25` (safety limit)
|
||||
- Respects `.bmignore` patterns
|
||||
|
||||
**Result:** Local and cloud are in sync. Baseline established.
|
||||
|
||||
**Why `--resync`?** This is an rclone requirement for the first bisync run. It establishes the initial state that future syncs will compare against. After the first sync, never use `--resync` unless you need to force a new baseline.
|
||||
|
||||
See: https://rclone.org/bisync/#resync
|
||||
```
|
||||
--resync
|
||||
This will effectively make both Path1 and Path2 filesystems contain a matching superset of all files. By default, Path2 files that do not exist in Path1 will be copied to Path1, and the process will then copy the Path1 tree to Path2.
|
||||
```
|
||||
|
||||
### 5. Subsequent Syncs
|
||||
|
||||
After the first sync, just run bisync without `--resync`:
|
||||
|
||||
```bash
|
||||
bm project bisync --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Rclone compares local and cloud states
|
||||
2. Syncs changes in both directions
|
||||
3. Auto-resolves conflicts (newer file wins)
|
||||
4. Updates `last_sync` timestamp in config
|
||||
|
||||
**Result:** Changes flow both ways - edit locally or in cloud, both stay in sync.
|
||||
|
||||
### 6. Verify Setup
|
||||
|
||||
Check status:
|
||||
|
||||
```bash
|
||||
bm cloud status
|
||||
```
|
||||
|
||||
You should see:
|
||||
- `OAuth: token valid` (or missing/expired)
|
||||
- `API Key: configured` (or not set)
|
||||
- `Cloud instance is healthy`
|
||||
- Instructions for project sync commands
|
||||
|
||||
## Working with Projects
|
||||
|
||||
### Understanding Project Commands
|
||||
|
||||
**Key concept:** Use regular `bm project` commands (not `bm cloud project`).
|
||||
|
||||
```bash
|
||||
# Local route
|
||||
bm project list --local
|
||||
bm project add research ~/Documents/research
|
||||
|
||||
# Cloud route
|
||||
bm project list --cloud
|
||||
bm project add research --cloud
|
||||
```
|
||||
|
||||
### Creating Projects
|
||||
|
||||
**Use case 1: Cloud-only project (no local sync)**
|
||||
|
||||
```bash
|
||||
bm project add temp-notes --cloud
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Creates project on cloud at `/app/data/temp-notes`
|
||||
- No local directory created
|
||||
- No sync configuration
|
||||
|
||||
**Result:** Project exists on cloud, accessible via MCP tools, but no local copy.
|
||||
|
||||
**Use case 2: Cloud project with local sync**
|
||||
|
||||
```bash
|
||||
bm project add research --cloud --local-path ~/Documents/research
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Creates project on cloud at `/app/data/research`
|
||||
- Creates local directory `~/Documents/research`
|
||||
- Stores sync config in `~/.basic-memory/config.json`
|
||||
- Prepares for bisync (but doesn't sync yet)
|
||||
|
||||
**Result:** Project ready to sync. Run `bm project bisync --name research --resync` to establish baseline.
|
||||
|
||||
**Use case 3: Add sync to existing cloud project**
|
||||
|
||||
```bash
|
||||
# Project already exists on cloud
|
||||
bm project sync-setup research ~/Documents/research
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Updates existing project's sync configuration
|
||||
- Creates local directory
|
||||
- Prepares for bisync
|
||||
|
||||
**Result:** Existing cloud project now has local sync path. Run bisync to pull files down.
|
||||
|
||||
### Listing Projects
|
||||
|
||||
View all projects:
|
||||
|
||||
```bash
|
||||
bm project list
|
||||
```
|
||||
|
||||
**What you see:**
|
||||
- Local projects always
|
||||
- Cloud projects when credentials are available
|
||||
- Default project marked
|
||||
- Route-related metadata (for example, local/cloud presence and sync info)
|
||||
|
||||
Example shape (single row for dual-presence projects):
|
||||
|
||||
```text
|
||||
Name Path Local Path Cloud Path CLI Default MCP (stdio)
|
||||
main /basic-memory ~/basic-memory /basic-memory local local
|
||||
specs /specs ~/dev/specs /specs cloud local
|
||||
```
|
||||
|
||||
### When a Project Exists in Both Local and Cloud
|
||||
|
||||
Use routing flags to disambiguate command targets:
|
||||
|
||||
```bash
|
||||
# Force local target for this command
|
||||
bm project info main --local
|
||||
bm project ls --name main --local
|
||||
|
||||
# Force cloud target for this command
|
||||
bm project info main --cloud
|
||||
bm project ls --name main --cloud
|
||||
```
|
||||
|
||||
Default behavior for no-project, no-flag commands is local.
|
||||
For MCP stdio, routing is always local.
|
||||
|
||||
## File Synchronization
|
||||
|
||||
### Understanding the Sync Commands
|
||||
|
||||
**There are three sync-related commands:**
|
||||
|
||||
1. `bm project sync` - One-way: local → cloud (make cloud match local)
|
||||
2. `bm project bisync` - Two-way: local ↔ cloud (recommended)
|
||||
3. `bm project check` - Verify files match (no changes)
|
||||
|
||||
### One-Way Sync: Local → Cloud
|
||||
|
||||
**Use case:** You made changes locally and want to push to cloud (overwrite cloud).
|
||||
|
||||
```bash
|
||||
bm project sync --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Reads files from `~/Documents/research` (local)
|
||||
2. Uses rclone sync to make cloud identical to local
|
||||
3. Respects `.bmignore` patterns
|
||||
4. Shows progress bar
|
||||
|
||||
**Result:** Cloud now matches local exactly. Any cloud-only changes are overwritten.
|
||||
|
||||
**When to use:**
|
||||
- You know local is the source of truth
|
||||
- You want to force cloud to match local
|
||||
- You don't care about cloud changes
|
||||
|
||||
### Two-Way Sync: Local ↔ Cloud (Recommended)
|
||||
|
||||
**Use case:** You edit files both locally and in cloud UI, want both to stay in sync.
|
||||
|
||||
```bash
|
||||
# First time - establish baseline
|
||||
bm project bisync --name research --resync
|
||||
|
||||
# Subsequent syncs
|
||||
bm project bisync --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Compares local and cloud states using bisync metadata
|
||||
2. Syncs changes in both directions
|
||||
3. Auto-resolves conflicts (newer file wins)
|
||||
4. Detects excessive deletes and fails safely (max 25 files)
|
||||
|
||||
**Conflict resolution example:**
|
||||
|
||||
```bash
|
||||
# Edit locally
|
||||
echo "Local change" > ~/Documents/research/notes.md
|
||||
|
||||
# Edit same file in cloud UI
|
||||
# Cloud now has: "Cloud change"
|
||||
|
||||
# Run bisync
|
||||
bm project bisync --name research
|
||||
|
||||
# Result: Newer file wins (based on modification time)
|
||||
# If cloud was more recent, cloud version kept
|
||||
# If local was more recent, local version kept
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Default workflow for most users
|
||||
- You edit in multiple places
|
||||
- You want automatic conflict resolution
|
||||
|
||||
### Verify Sync Integrity
|
||||
|
||||
**Use case:** Check if local and cloud match without making changes.
|
||||
|
||||
```bash
|
||||
bm project check --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Compares file checksums between local and cloud
|
||||
2. Reports differences
|
||||
3. No files transferred
|
||||
|
||||
**Result:** Shows which files differ. Run bisync to sync them.
|
||||
|
||||
```bash
|
||||
# One-way check (faster)
|
||||
bm project check --name research --one-way
|
||||
```
|
||||
|
||||
### Preview Changes (Dry Run)
|
||||
|
||||
**Use case:** See what would change without actually syncing.
|
||||
|
||||
```bash
|
||||
bm project bisync --name research --dry-run
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Runs bisync logic
|
||||
2. Shows what would be transferred/deleted
|
||||
3. No actual changes made
|
||||
|
||||
**Result:** Safe preview of sync operations.
|
||||
|
||||
### Advanced: List Project Files by Route
|
||||
|
||||
**Use case:** Inspect local or cloud project files explicitly.
|
||||
|
||||
```bash
|
||||
# List local project files (default target when no route flag is given)
|
||||
bm project ls --name research
|
||||
bm project ls --name research --local
|
||||
|
||||
# List cloud project files
|
||||
bm project ls --name research --cloud
|
||||
|
||||
# List files in subdirectory
|
||||
bm project ls --name research --cloud --path subfolder
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Resolves route from flags (or local default when no route is given)
|
||||
2. Lists files for the chosen project instance
|
||||
3. No files transferred
|
||||
|
||||
**Result:** See file listing for the target route.
|
||||
|
||||
## Multiple Projects
|
||||
|
||||
### Syncing Multiple Projects
|
||||
|
||||
**Use case:** You have several projects with local sync, want to sync all at once.
|
||||
|
||||
```bash
|
||||
# Setup multiple projects
|
||||
bm project add research --cloud --local-path ~/Documents/research
|
||||
bm project add work --cloud --local-path ~/work-notes
|
||||
bm project add personal --cloud --local-path ~/personal
|
||||
|
||||
# Establish baselines
|
||||
bm project bisync --name research --resync
|
||||
bm project bisync --name work --resync
|
||||
bm project bisync --name personal --resync
|
||||
|
||||
# Daily workflow: sync everything
|
||||
bm project bisync --name research
|
||||
bm project bisync --name work
|
||||
bm project bisync --name personal
|
||||
```
|
||||
|
||||
**Future:** `--all` flag will sync all configured projects:
|
||||
|
||||
```bash
|
||||
bm project bisync --all # Coming soon
|
||||
```
|
||||
|
||||
### Mixed Usage
|
||||
|
||||
**Use case:** Some projects sync, some stay cloud-only.
|
||||
|
||||
```bash
|
||||
# Projects with sync
|
||||
bm project add research --cloud --local-path ~/Documents/research
|
||||
bm project add work --cloud --local-path ~/work
|
||||
|
||||
# Cloud-only projects
|
||||
bm project add archive --cloud
|
||||
bm project add temp-notes --cloud
|
||||
|
||||
# Sync only the configured ones
|
||||
bm project bisync --name research
|
||||
bm project bisync --name work
|
||||
|
||||
# Archive and temp-notes stay cloud-only
|
||||
```
|
||||
|
||||
**Result:** Fine-grained control over what syncs.
|
||||
|
||||
## Per-Project Cloud Routing (API Key)
|
||||
|
||||
Route individual projects through cloud using an API key. This lets you keep some projects local while others route through cloud.
|
||||
|
||||
### Setting Up API Key Auth
|
||||
|
||||
**Option A: Create a key in the web app, then save it locally:**
|
||||
|
||||
```bash
|
||||
bm cloud set-key bmc_abc123...
|
||||
```
|
||||
|
||||
**Option B: Create a key via CLI (requires OAuth login first):**
|
||||
|
||||
```bash
|
||||
bm cloud login # One-time OAuth login
|
||||
bm cloud create-key "my-laptop" # Creates key and saves it locally
|
||||
```
|
||||
|
||||
The API key is account-level — it grants access to all your cloud projects. It's stored in `~/.basic-memory/config.json` as `cloud_api_key`.
|
||||
|
||||
### Setting Project Modes
|
||||
|
||||
```bash
|
||||
# Route a project through cloud
|
||||
bm project set-cloud research
|
||||
|
||||
# Revert to local mode
|
||||
bm project set-local research
|
||||
|
||||
# View project modes
|
||||
bm project list
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
- `set-cloud`: validates the API key exists, then sets the project mode to `cloud` in config
|
||||
- `set-local`: reverts the project to local mode (removes the mode entry from config)
|
||||
- MCP tools and CLI commands for that project will route to `cloud_host/proxy` with the API key as Bearer token
|
||||
|
||||
### How It Works
|
||||
|
||||
When an MCP tool or CLI command runs for a cloud-mode project:
|
||||
|
||||
1. `get_client(project_name="research")` checks the project's mode in config
|
||||
2. If mode is `cloud`, creates an HTTP client pointed at `cloud_host/proxy` with `Authorization: Bearer bmc_...`
|
||||
3. If mode is `local` (default), uses the in-process ASGI transport as usual
|
||||
|
||||
**Routing priority** (highest to lowest):
|
||||
1. Factory injection (cloud app, tests)
|
||||
2. Explicit route override (`--local` / `--cloud`)
|
||||
3. Per-project cloud mode (API key)
|
||||
4. Local ASGI transport (default)
|
||||
|
||||
Route override environment variables:
|
||||
- `BASIC_MEMORY_FORCE_LOCAL=true`
|
||||
- `BASIC_MEMORY_FORCE_CLOUD=true`
|
||||
- `BASIC_MEMORY_EXPLICIT_ROUTING=true`
|
||||
|
||||
No-project, no-flag CLI commands default to local routing.
|
||||
|
||||
### Configuration Example
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": {
|
||||
"personal": "/Users/me/notes",
|
||||
"research": "/Users/me/research"
|
||||
},
|
||||
"project_modes": {
|
||||
"research": "cloud"
|
||||
},
|
||||
"cloud_api_key": "bmc_abc123...",
|
||||
"cloud_host": "https://cloud.basicmemory.com",
|
||||
"default_project": "personal"
|
||||
}
|
||||
```
|
||||
|
||||
In this example, `personal` stays local and `research` routes through cloud. Projects not listed in `project_modes` default to local.
|
||||
|
||||
### Sync Behavior
|
||||
|
||||
Cloud-mode projects are automatically skipped during local file sync (background sync and file watching). Their files live on the cloud instance, not locally.
|
||||
|
||||
## OAuth Logout
|
||||
|
||||
```bash
|
||||
bm cloud logout
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
1. Removes stored OAuth token(s)
|
||||
2. Does not change per-project route configuration
|
||||
3. Does not change command routing defaults
|
||||
|
||||
**Result:** OAuth session is cleared. API-key-based routing still works if `cloud_api_key` is configured.
|
||||
|
||||
## Filter Configuration
|
||||
|
||||
### Understanding .bmignore
|
||||
|
||||
**The problem:** You don't want to sync everything (e.g., `.git`, `node_modules`, database files).
|
||||
|
||||
**The solution:** `.bmignore` file with gitignore-style patterns.
|
||||
|
||||
**Location:** `~/.basic-memory/.bmignore`
|
||||
|
||||
**Default patterns:**
|
||||
|
||||
```gitignore
|
||||
# Version control
|
||||
.git/**
|
||||
|
||||
# Python
|
||||
__pycache__/**
|
||||
*.pyc
|
||||
.venv/**
|
||||
venv/**
|
||||
|
||||
# Node.js
|
||||
node_modules/**
|
||||
|
||||
# Basic Memory internals
|
||||
memory.db/**
|
||||
memory.db-shm/**
|
||||
memory.db-wal/**
|
||||
config.json/**
|
||||
watch-status.json/**
|
||||
.bmignore.rclone/**
|
||||
|
||||
# OS files
|
||||
.DS_Store/**
|
||||
Thumbs.db/**
|
||||
|
||||
# Environment files
|
||||
.env/**
|
||||
.env.local/**
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
1. On first sync, `.bmignore` created with defaults
|
||||
2. Patterns converted to rclone filter format (`.bmignore.rclone`)
|
||||
3. Rclone uses filters during sync
|
||||
4. Same patterns used by all projects
|
||||
|
||||
**Customizing:**
|
||||
|
||||
```bash
|
||||
# Edit patterns
|
||||
code ~/.basic-memory/.bmignore
|
||||
|
||||
# Add custom patterns
|
||||
echo "*.tmp/**" >> ~/.basic-memory/.bmignore
|
||||
|
||||
# Next sync uses updated patterns
|
||||
bm project bisync --name research
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**Solution:**
|
||||
1. Visit subscribe URL shown in error
|
||||
2. Sign up for subscription
|
||||
3. Run `bm cloud login` again
|
||||
|
||||
**Note:** Access is immediate when subscription becomes active.
|
||||
|
||||
### Bisync Initialization
|
||||
|
||||
**Problem:** "First bisync requires --resync"
|
||||
|
||||
**Explanation:** Bisync needs a baseline state before it can sync changes.
|
||||
|
||||
**Solution:**
|
||||
|
||||
```bash
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Establishes initial sync state
|
||||
- Creates baseline in `~/.basic-memory/bisync-state/research/`
|
||||
- Syncs all files bidirectionally
|
||||
|
||||
**Result:** Future syncs work without `--resync`.
|
||||
|
||||
### Empty Directory Issues
|
||||
|
||||
**Problem:** "Empty prior Path1 listing. Cannot sync to an empty directory"
|
||||
|
||||
**Explanation:** Rclone bisync doesn't work well with completely empty directories. It needs at least one file to establish a baseline.
|
||||
|
||||
**Solution:** Add at least one file before running `--resync`:
|
||||
|
||||
```bash
|
||||
# Create a placeholder file
|
||||
echo "# Research Notes" > ~/Documents/research/README.md
|
||||
|
||||
# Now run bisync
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**Why this happens:** Bisync creates listing files that track the state of each side. When both directories are completely empty, these listing files are considered invalid by rclone.
|
||||
|
||||
**Best practice:** Always have at least one file (like a README.md) in your project directory before setting up sync.
|
||||
|
||||
### Bisync State Corruption
|
||||
|
||||
**Problem:** Bisync fails with errors about corrupted state or listing files
|
||||
|
||||
**Explanation:** Sometimes bisync state can become inconsistent (e.g., after mixing dry-run and actual runs, or after manual file operations).
|
||||
|
||||
**Solution:** Clear bisync state and re-establish baseline:
|
||||
|
||||
```bash
|
||||
# Clear bisync state
|
||||
bm project bisync-reset research
|
||||
|
||||
# Re-establish baseline
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Removes all bisync metadata from `~/.basic-memory/bisync-state/research/`
|
||||
- Forces fresh baseline on next `--resync`
|
||||
- Safe operation (doesn't touch your files)
|
||||
|
||||
**Note:** This command also runs automatically when you remove a project to clean up state directories.
|
||||
|
||||
### Too Many Deletes
|
||||
|
||||
**Problem:** "Error: max delete limit (25) exceeded"
|
||||
|
||||
**Explanation:** Bisync detected you're about to delete more than 25 files. This is a safety check to prevent accidents.
|
||||
|
||||
**Solution 1:** Review what you're deleting, then force resync:
|
||||
|
||||
```bash
|
||||
# Check what would be deleted
|
||||
bm project bisync --name research --dry-run
|
||||
|
||||
# If correct, establish new baseline
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**Solution 2:** Use one-way sync if you know local is correct:
|
||||
|
||||
```bash
|
||||
bm project sync --name research
|
||||
```
|
||||
|
||||
### Project Not Configured for Sync
|
||||
|
||||
**Problem:** "Project research has no local_sync_path configured"
|
||||
|
||||
**Explanation:** Project exists on cloud but has no local sync path.
|
||||
|
||||
**Solution:**
|
||||
|
||||
```bash
|
||||
bm project sync-setup research ~/Documents/research
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
### Connection Issues
|
||||
|
||||
**Problem:** "Cannot connect to cloud instance"
|
||||
|
||||
**Solution:** Check status:
|
||||
|
||||
```bash
|
||||
bm cloud status
|
||||
```
|
||||
|
||||
If instance is down, wait a few minutes and retry.
|
||||
|
||||
## Security
|
||||
|
||||
- **Authentication**: OAuth 2.1 with PKCE flow
|
||||
- **Tokens**: Stored securely in `~/.basic-memory/basic-memory-cloud.json`
|
||||
- **Transport**: All data encrypted in transit (HTTPS)
|
||||
- **Credentials**: Scoped S3 credentials (read-write to your tenant only)
|
||||
- **Isolation**: Your data isolated from other tenants
|
||||
- **Ignore patterns**: Sensitive files automatically excluded via `.bmignore`
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Cloud Authentication
|
||||
|
||||
```bash
|
||||
bm cloud login # Authenticate and store OAuth credentials
|
||||
bm cloud logout # Remove stored OAuth credentials
|
||||
bm cloud status # Check auth state and instance health
|
||||
bm cloud promo --off # Disable CLI cloud promo notices
|
||||
```
|
||||
|
||||
### API Key Management
|
||||
|
||||
```bash
|
||||
bm cloud set-key <key> # Save a cloud API key (bmc_ prefixed)
|
||||
bm cloud create-key <name> # Create API key via cloud API (requires OAuth login)
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
bm cloud setup # Install rclone and configure credentials
|
||||
```
|
||||
|
||||
### Project Management
|
||||
|
||||
```bash
|
||||
bm project list --local # Local project list
|
||||
bm project list --cloud # Cloud project list
|
||||
bm project add <name> --cloud # Create cloud project (no sync)
|
||||
bm project add <name> --cloud --local-path <path> # Create with local sync
|
||||
bm project sync-setup <name> <path> # Add sync to existing project
|
||||
bm project rm <name> # Delete project
|
||||
```
|
||||
|
||||
### Per-Project Routing
|
||||
|
||||
```bash
|
||||
bm project set-cloud <name> # Route project through cloud (requires API key)
|
||||
bm project set-local <name> # Revert project to local mode
|
||||
```
|
||||
|
||||
### File Synchronization
|
||||
|
||||
```bash
|
||||
# One-way sync (local → cloud)
|
||||
bm project sync --name <project>
|
||||
bm project sync --name <project> --dry-run
|
||||
bm project sync --name <project> --verbose
|
||||
|
||||
# Two-way sync (local ↔ cloud) - Recommended
|
||||
bm project bisync --name <project> # After first --resync
|
||||
bm project bisync --name <project> --resync # First time / force baseline
|
||||
bm project bisync --name <project> --dry-run
|
||||
bm project bisync --name <project> --verbose
|
||||
|
||||
# Integrity check
|
||||
bm project check --name <project>
|
||||
bm project check --name <project> --one-way
|
||||
|
||||
# List project files by route
|
||||
bm project ls --name <project> # Default target: local
|
||||
bm project ls --name <project> --local
|
||||
bm project ls --name <project> --cloud
|
||||
bm project ls --name <project> --cloud --path <subpath>
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Basic Memory Cloud uses project-scoped sync:**
|
||||
|
||||
1. **Authenticate cloud access** - `bm cloud login`
|
||||
2. **Install rclone** - `bm cloud setup`
|
||||
3. **Add projects with sync** - `bm project add research --cloud --local-path ~/Documents/research`
|
||||
4. **Preview first sync** - `bm project bisync --name research --resync --dry-run`
|
||||
5. **Establish baseline** - `bm project bisync --name research --resync`
|
||||
6. **Daily workflow** - `bm project bisync --name research`
|
||||
|
||||
**Key benefits:**
|
||||
- ✅ Each project independently syncs (or doesn't)
|
||||
- ✅ Projects can live anywhere on disk
|
||||
- ✅ Explicit sync operations (no magic)
|
||||
- ✅ Safe by design (max delete limits, conflict resolution)
|
||||
- ✅ Full offline access (work locally, sync when ready)
|
||||
|
||||
**Future enhancements:**
|
||||
- `--all` flag to sync all configured projects
|
||||
- Project list showing sync status
|
||||
- Watch mode for automatic sync
|
||||
@@ -0,0 +1,91 @@
|
||||
# Cloud Semantic Search Value (Customer-Facing Technical Story)
|
||||
|
||||
This document explains why teams should buy cloud semantic search even when local search exists.
|
||||
|
||||
## Core Promise
|
||||
|
||||
Markdown files remain the source of truth in both local and cloud modes.
|
||||
|
||||
- Files are portable.
|
||||
- Search indexes are derived and rebuildable.
|
||||
- You never get locked into proprietary document storage.
|
||||
|
||||
## The Customer Problem
|
||||
|
||||
Teams paying for cloud are usually not optimizing for "can this run locally." They are optimizing for:
|
||||
|
||||
- finding the right note the first time,
|
||||
- keeping retrieval quality high as note volume grows,
|
||||
- avoiding search slowdowns while content is actively changing,
|
||||
- getting consistent results across users, agents, and sessions.
|
||||
|
||||
## Why Cloud Is the Aspirin
|
||||
|
||||
Cloud semantic search is the immediate pain reliever because it fixes the problems users feel right now.
|
||||
|
||||
### 1) Better hit rate on real queries
|
||||
|
||||
Cloud uses stronger managed embeddings than the default local model, which improves semantic recall for paraphrases and vague questions.
|
||||
|
||||
Customer outcome:
|
||||
|
||||
- fewer "I know this exists but search missed it" moments,
|
||||
- less query rewording,
|
||||
- faster time to answer.
|
||||
|
||||
### 2) Better behavior under active workloads
|
||||
|
||||
Cloud indexing runs out of band in workers, so indexing does not compete with interactive read/write traffic.
|
||||
|
||||
Customer outcome:
|
||||
|
||||
- stable search responsiveness during heavy updates,
|
||||
- fresher semantic results shortly after edits,
|
||||
- less user-visible performance variance.
|
||||
|
||||
### 3) Better consistency for shared knowledge
|
||||
|
||||
Cloud retrieval runs against a centralized tenant index, so teams and agents resolve against the same semantic state.
|
||||
|
||||
Customer outcome:
|
||||
|
||||
- fewer "works on my machine" search differences,
|
||||
- more predictable agent behavior across environments,
|
||||
- easier cross-user collaboration on large knowledge bases.
|
||||
|
||||
### 4) Better quality at higher scale
|
||||
|
||||
With Postgres + `pgvector` per tenant, cloud can sustain larger note collections and higher query volumes than typical local setups.
|
||||
|
||||
Customer outcome:
|
||||
|
||||
- confidence as repositories grow to tens of thousands of notes,
|
||||
- less need for user-side tuning,
|
||||
- fewer quality regressions as usage increases.
|
||||
|
||||
## Local Is the Vitamin
|
||||
|
||||
Local semantic search still matters and should stay strong.
|
||||
|
||||
- offline use,
|
||||
- privacy-first operation,
|
||||
- no cloud dependency,
|
||||
- user-controlled runtime.
|
||||
|
||||
It compounds long-term ownership and resilience, but does not remove the immediate pain points cloud solves for teams at scale.
|
||||
|
||||
## Recommended Messaging
|
||||
|
||||
One-liner:
|
||||
|
||||
"Cloud semantic search is the aspirin: it fixes retrieval quality and performance pain now. Local semantic search is the vitamin: it builds long-term control and resilience."
|
||||
|
||||
Long form:
|
||||
|
||||
"Basic Memory keeps markdown as the source of truth everywhere. Local gives privacy and offline control. Cloud adds immediate, measurable improvements in search quality, consistency, and responsiveness for teams and agents running at scale."
|
||||
|
||||
## Packaging Guidance
|
||||
|
||||
- Base: local FTS plus optional local semantic search.
|
||||
- Cloud value: higher semantic quality, stable performance under load, and consistent team-wide retrieval.
|
||||
- Keep interfaces pluggable (`EmbeddingProvider`, vector backend protocol) so implementation can evolve without changing user workflows.
|
||||
@@ -0,0 +1,138 @@
|
||||
# MCP UI Bakeoff - Instructions & Test Plan
|
||||
|
||||
Last updated: 2026-02-02
|
||||
|
||||
## Scope
|
||||
|
||||
Compare three presentation paths for Basic Memory MCP tools:
|
||||
|
||||
1. **Tool‑UI (React)** via MCP App resources.
|
||||
2. **MCP‑UI Python SDK** embedded UI resources (legacy host path).
|
||||
3. **ASCII/ANSI** output for TUI clients.
|
||||
|
||||
This doc is the running instruction set and test plan. Update as implementation progresses.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Repo: `basic-memory` (worktree: `basic-memory-mcp-ui-poc`)
|
||||
- Node for tool‑ui build (already used for POC)
|
||||
- Python 3.12+ with `uv`
|
||||
|
||||
Optional (for MCP‑UI Python SDK path):
|
||||
|
||||
- Local repo: `/Users/phernandez/dev/mcp-ui`
|
||||
- Install the server SDK into the Basic Memory venv:
|
||||
- `uv pip install -e /Users/phernandez/dev/mcp-ui/sdks/python/server`
|
||||
|
||||
---
|
||||
|
||||
## Build / Refresh Steps
|
||||
|
||||
### Tool‑UI React bundle
|
||||
|
||||
```bash
|
||||
cd ui/tool-ui-react
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
This regenerates:
|
||||
|
||||
- `src/basic_memory/mcp/ui/html/search-results-tool-ui.html`
|
||||
- `src/basic_memory/mcp/ui/html/note-preview-tool-ui.html`
|
||||
|
||||
---
|
||||
|
||||
## How to Run the MCP Server
|
||||
|
||||
```bash
|
||||
basic-memory mcp --transport stdio
|
||||
```
|
||||
|
||||
Optional to pick UI variant for MCP App resources:
|
||||
|
||||
```bash
|
||||
export BASIC_MEMORY_MCP_UI_VARIANT=tool-ui # or vanilla | mcp-ui
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test Cases
|
||||
|
||||
### 1) MCP App Resource UI (tool‑ui / vanilla / mcp‑ui)
|
||||
|
||||
Tools:
|
||||
- `search_notes`
|
||||
- `read_note`
|
||||
|
||||
Expect:
|
||||
- Tool meta points to `ui://basic-memory/search-results` and `ui://basic-memory/note-preview`
|
||||
- Resource content differs by `BASIC_MEMORY_MCP_UI_VARIANT`
|
||||
- Variant‑specific URIs also available:
|
||||
- `ui://basic-memory/search-results/vanilla`
|
||||
- `ui://basic-memory/search-results/tool-ui`
|
||||
- `ui://basic-memory/search-results/mcp-ui`
|
||||
- `ui://basic-memory/note-preview/vanilla`
|
||||
- `ui://basic-memory/note-preview/tool-ui`
|
||||
- `ui://basic-memory/note-preview/mcp-ui`
|
||||
|
||||
Manual check:
|
||||
- Trigger tool in MCP‑App‑capable host and confirm UI renders.
|
||||
|
||||
---
|
||||
|
||||
### 2) Text / JSON Output Modes
|
||||
|
||||
Tools:
|
||||
- `search_notes(output_format="text" | "json")`
|
||||
- `read_note(output_format="text" | "json")`
|
||||
- `write_note(output_format="text" | "json")`
|
||||
- `edit_note(output_format="text" | "json")`
|
||||
- `recent_activity(output_format="text" | "json")`
|
||||
- `list_memory_projects(output_format="text" | "json")`
|
||||
- `create_memory_project(output_format="text" | "json")`
|
||||
- `delete_note(output_format="text" | "json")`
|
||||
- `move_note(output_format="text" | "json")`
|
||||
- `build_context(output_format="json" | "text")`
|
||||
|
||||
Expect:
|
||||
- `text` mode preserves existing human-readable responses.
|
||||
- `json` mode returns structured dict/list payloads for machine-readable clients.
|
||||
|
||||
Automated:
|
||||
- `uv run pytest test-int/mcp/test_output_format_json_integration.py`
|
||||
|
||||
---
|
||||
|
||||
### 3) MCP‑UI Python SDK (embedded UI resource)
|
||||
|
||||
Tools (embedded resource responses):
|
||||
- `search_notes_ui` (MCP‑UI SDK)
|
||||
- `read_note_ui` (MCP‑UI SDK)
|
||||
|
||||
Expected output:
|
||||
- Tool response content contains an EmbeddedResource (`type: "resource"`)
|
||||
- `mimeType` is `text/html`
|
||||
- `_meta` includes:
|
||||
- `mcpui.dev/ui-preferred-frame-size`
|
||||
- `mcpui.dev/ui-initial-render-data`
|
||||
|
||||
Manual check:
|
||||
- Render tool responses using `UIResourceRenderer` (legacy host flow).
|
||||
|
||||
Automated (if SDK installed):
|
||||
- `uv run pytest test-int/mcp/test_ui_sdk_integration.py`
|
||||
|
||||
---
|
||||
|
||||
## Bakeoff Notes Template
|
||||
|
||||
Fill in after running:
|
||||
|
||||
- Tool‑UI (React): __
|
||||
- MCP‑UI SDK (embedded): __
|
||||
- Text/JSON modes: __
|
||||
|
||||
Decision + rationale: __
|
||||
@@ -0,0 +1,260 @@
|
||||
# Metadata Search Reference
|
||||
|
||||
Basic Memory automatically indexes custom frontmatter fields so you can query them with structured filters. Any YAML key in a note's frontmatter beyond the standard set (`title`, `type`, `tags`, `permalink`, `schema`) is stored as `entity_metadata` and becomes searchable.
|
||||
|
||||
## Querying with `search_notes`
|
||||
|
||||
`search_notes` is the single search tool for all queries — text, metadata filters, or both. The `query` parameter is optional, so you can use metadata filters alone without passing an empty string.
|
||||
|
||||
## Filter Syntax
|
||||
|
||||
Filters are a JSON dictionary where each key targets a frontmatter field and the value specifies the match condition. Multiple keys combine with **AND** logic — every filter must match.
|
||||
|
||||
### Equality
|
||||
|
||||
Match a single value exactly.
|
||||
|
||||
```json
|
||||
{"status": "active"}
|
||||
```
|
||||
|
||||
Finds notes whose frontmatter contains `status: active`.
|
||||
|
||||
### Array Contains (all)
|
||||
|
||||
Pass a list to require **all** listed values to be present in the field.
|
||||
|
||||
```json
|
||||
{"tags": ["security", "oauth"]}
|
||||
```
|
||||
|
||||
Finds notes tagged with both `security` and `oauth`.
|
||||
|
||||
### `$in` (any of)
|
||||
|
||||
Match if the field equals **any** value in the list.
|
||||
|
||||
```json
|
||||
{"priority": {"$in": ["high", "critical"]}}
|
||||
```
|
||||
|
||||
### `$gt`, `$gte`, `$lt`, `$lte`
|
||||
|
||||
Numeric and text comparisons. Numeric values use numeric comparison; strings use lexicographic comparison.
|
||||
|
||||
```json
|
||||
{"confidence": {"$gt": 0.7}}
|
||||
{"score": {"$lte": 100}}
|
||||
```
|
||||
|
||||
### `$between`
|
||||
|
||||
Range filter (inclusive). Takes a `[min, max]` pair.
|
||||
|
||||
```json
|
||||
{"score": {"$between": [0.3, 0.8]}}
|
||||
```
|
||||
|
||||
### Nested Access (dot notation)
|
||||
|
||||
Access nested frontmatter values using dots.
|
||||
|
||||
```json
|
||||
{"schema.version": "2"}
|
||||
```
|
||||
|
||||
This queries the `version` key inside a `schema` object in frontmatter.
|
||||
|
||||
### Summary Table
|
||||
|
||||
| Operator | Syntax | Example |
|
||||
|----------|--------|---------|
|
||||
| Equality | `{"field": "value"}` | `{"status": "active"}` |
|
||||
| Array contains (all) | `{"field": ["a", "b"]}` | `{"tags": ["security", "oauth"]}` |
|
||||
| `$in` (any of) | `{"field": {"$in": [...]}}` | `{"priority": {"$in": ["high", "critical"]}}` |
|
||||
| `$gt` / `$gte` | `{"field": {"$gt": N}}` | `{"confidence": {"$gt": 0.7}}` |
|
||||
| `$lt` / `$lte` | `{"field": {"$lt": N}}` | `{"score": {"$lt": 0.5}}` |
|
||||
| `$between` | `{"field": {"$between": [min, max]}}` | `{"score": {"$between": [0.3, 0.8]}}` |
|
||||
| Nested access | `{"a.b": "value"}` | `{"schema.version": "2"}` |
|
||||
|
||||
**Key rules:**
|
||||
- Filter keys must match `[A-Za-z0-9_-]+` (dots separate nesting levels).
|
||||
- Each operator dict must contain exactly one operator.
|
||||
- `$in` and array-contains require non-empty lists.
|
||||
- `$between` requires exactly two values `[min, max]`.
|
||||
|
||||
## MCP Tool — `search_notes`
|
||||
|
||||
`search_notes` is the single search tool for text queries, metadata filters, or both. The `query` parameter is optional.
|
||||
|
||||
**Relevant parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `query` | string (optional) | Text search query. Omit for filter-only searches. |
|
||||
| `metadata_filters` | dict | Structured filter dict (see syntax above) |
|
||||
| `tags` | list[str] | Convenience shorthand — merged into `metadata_filters["tags"]` |
|
||||
| `status` | string | Convenience shorthand — merged into `metadata_filters["status"]` |
|
||||
|
||||
**Merging rules:** `tags` and `status` are convenience shortcuts. They are merged into `metadata_filters` using `setdefault` — if the same key already exists in `metadata_filters`, the explicit filter wins.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```python
|
||||
# Text search filtered by metadata
|
||||
await search_notes("authentication", metadata_filters={"status": "draft"})
|
||||
|
||||
# Filter-only search (no query needed)
|
||||
await search_notes(metadata_filters={"type": "spec"})
|
||||
|
||||
# Combine text, tags shortcut, and metadata
|
||||
await search_notes(
|
||||
"oauth flow",
|
||||
tags=["security"],
|
||||
metadata_filters={"confidence": {"$gt": 0.7}},
|
||||
)
|
||||
|
||||
# Convenience shortcuts
|
||||
await search_notes("planning", status="active")
|
||||
await search_notes(tags=["tier1", "alpha"])
|
||||
```
|
||||
|
||||
## Tag Search Shortcuts
|
||||
|
||||
The `tag:` prefix in a search query is a shorthand for tag-based metadata filtering. When `search_notes` receives a query starting with `tag:`, it converts the query into a `tags` filter and clears the text query.
|
||||
|
||||
```python
|
||||
# These are equivalent:
|
||||
await search_notes("tag:tier1")
|
||||
await search_notes("", tags=["tier1"])
|
||||
|
||||
# Multiple tags (comma or space separated) — all must be present:
|
||||
await search_notes("tag:tier1,alpha")
|
||||
await search_notes("tag:tier1 alpha")
|
||||
```
|
||||
|
||||
## CLI Access
|
||||
|
||||
The `bm tool search-notes` command exposes metadata filtering via `--meta` and `--filter` flags.
|
||||
|
||||
### `--meta` — simple key=value filters
|
||||
|
||||
Repeatable flag for equality filters on frontmatter fields.
|
||||
|
||||
```bash
|
||||
# Single filter
|
||||
bm tool search-notes "my query" --meta status=draft
|
||||
|
||||
# Multiple filters (AND logic)
|
||||
bm tool search-notes "" --meta status=active --meta priority=high
|
||||
```
|
||||
|
||||
### `--filter` — advanced JSON filters
|
||||
|
||||
Pass a full JSON filter dictionary for operator-based queries.
|
||||
|
||||
```bash
|
||||
# Range filter
|
||||
bm tool search-notes "" --filter '{"score": {"$between": [0.3, 0.8]}}'
|
||||
|
||||
# $in filter
|
||||
bm tool search-notes "" --filter '{"priority": {"$in": ["high", "critical"]}}'
|
||||
```
|
||||
|
||||
### `--tag` and `--status` — convenience shortcuts
|
||||
|
||||
```bash
|
||||
bm tool search-notes "query" --tag security --tag oauth
|
||||
bm tool search-notes "" --status draft
|
||||
```
|
||||
|
||||
### Combined example
|
||||
|
||||
```bash
|
||||
bm tool search-notes "authentication" --tag security --meta status=draft --type spec
|
||||
```
|
||||
|
||||
## Practical Examples
|
||||
|
||||
### Example notes with custom frontmatter
|
||||
|
||||
**`specs/auth-design.md`:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Auth Design
|
||||
type: spec
|
||||
tags: [security, oauth]
|
||||
status: in-progress
|
||||
priority: high
|
||||
confidence: 0.85
|
||||
---
|
||||
|
||||
# Auth Design
|
||||
|
||||
## Observations
|
||||
- [decision] Use OAuth 2.1 with PKCE for all client types #security
|
||||
- [requirement] Token refresh must be transparent to the user
|
||||
|
||||
## Relations
|
||||
- implements [[Security Requirements]]
|
||||
```
|
||||
|
||||
**`specs/search-redesign.md`:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Search Redesign
|
||||
type: spec
|
||||
tags: [search, performance]
|
||||
status: draft
|
||||
priority: medium
|
||||
confidence: 0.6
|
||||
---
|
||||
|
||||
# Search Redesign
|
||||
|
||||
## Observations
|
||||
- [goal] Sub-100ms search response times #performance
|
||||
- [approach] Hybrid FTS + vector retrieval
|
||||
|
||||
## Relations
|
||||
- depends_on [[Database Schema]]
|
||||
```
|
||||
|
||||
### Queries that find them
|
||||
|
||||
```python
|
||||
# Find all in-progress specs
|
||||
await search_notes(metadata_filters={"status": "in-progress", "type": "spec"})
|
||||
# → Auth Design
|
||||
|
||||
# Find high-confidence specs
|
||||
await search_notes(metadata_filters={"confidence": {"$gt": 0.7}})
|
||||
# → Auth Design (confidence: 0.85)
|
||||
|
||||
# Find specs with priority high or medium
|
||||
await search_notes(metadata_filters={"priority": {"$in": ["high", "medium"]}})
|
||||
# → Auth Design, Search Redesign
|
||||
|
||||
# Find specs in a confidence range
|
||||
await search_notes(metadata_filters={"confidence": {"$between": [0.5, 0.9]}})
|
||||
# → Auth Design (0.85), Search Redesign (0.6)
|
||||
|
||||
# Find notes tagged with security
|
||||
await search_notes("tag:security")
|
||||
# → Auth Design
|
||||
|
||||
# Combined: text search + metadata filter
|
||||
await search_notes("OAuth", metadata_filters={"status": "in-progress"})
|
||||
# → Auth Design
|
||||
```
|
||||
|
||||
### CLI equivalents
|
||||
|
||||
```bash
|
||||
bm tool search-notes "" --meta status=in-progress --type spec
|
||||
bm tool search-notes "" --filter '{"confidence": {"$gt": 0.7}}'
|
||||
bm tool search-notes "OAuth" --meta status=in-progress
|
||||
bm tool search-notes --tag security
|
||||
```
|
||||
@@ -0,0 +1,344 @@
|
||||
# Post-v0.18.0 Test Plan and Acceptance Criteria
|
||||
|
||||
## Goal
|
||||
|
||||
Define a complete validation plan for all major features merged after `v0.18.0`, combining:
|
||||
|
||||
- Coverage-gap-driven automated tests
|
||||
- Real MCP server integration tests (no mocks for target flows)
|
||||
- Manual MCP verification via LLM-driven tool calls
|
||||
|
||||
This plan is based on commits in `v0.18.0..HEAD` and the latest `just check` coverage output.
|
||||
|
||||
## Scope Window
|
||||
|
||||
- Start tag: `v0.18.0` (2026-01-28)
|
||||
- End: current `main`
|
||||
- Change volume: 12 feature commits + 14 bug-fix commits (+ release chores/hotfixes)
|
||||
|
||||
## Execution Strategy
|
||||
|
||||
1. Stabilize all feature-level acceptance criteria in automated tests first.
|
||||
2. Add black-box MCP integration tests for semantic search + schema (real server startup).
|
||||
3. Run manual MCP tool-call verification to confirm real UX and routing behavior.
|
||||
4. Re-run full gate: `just check` + targeted integration packs.
|
||||
|
||||
## Global Quality Gates
|
||||
|
||||
- Feature criteria below must all pass.
|
||||
- No regressions in existing suites.
|
||||
- Coverage improves in targeted low-coverage feature modules.
|
||||
- SQLite and Postgres parity for search/semantic features.
|
||||
|
||||
## Priority Coverage Gaps (from latest run)
|
||||
|
||||
These are the most important post-`v0.18.0` feature modules currently under-covered:
|
||||
|
||||
- `src/basic_memory/mcp/tools/schema.py` (27%)
|
||||
- `src/basic_memory/mcp/clients/schema.py` (36%)
|
||||
- `src/basic_memory/mcp/tools/ui_sdk.py` (43%)
|
||||
- `src/basic_memory/mcp/tools/search.py` (73%)
|
||||
- `src/basic_memory/repository/postgres_search_repository.py` (63%)
|
||||
- `src/basic_memory/mcp/async_client.py` (82%)
|
||||
- `src/basic_memory/api/v2/routers/schema_router.py` (80%)
|
||||
|
||||
## Feature Acceptance Criteria and Test Plan
|
||||
|
||||
### 1) Schema System (`c97733d`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- `schema_validate`, `schema_infer`, and `schema_diff` produce consistent outcomes across CLI/API/MCP for the same fixture set.
|
||||
- Strict validation fails deterministically on required-field/type violations.
|
||||
- Validation warnings are stable and machine-readable in non-strict mode.
|
||||
- Inference output is deterministic for unchanged input corpus.
|
||||
- Drift diff output is deterministic and identifies missing/extra/type-mismatch fields correctly.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/schema/*`
|
||||
- `tests/api/v2/test_schema_router.py`
|
||||
- `test-int/test_schema/*`
|
||||
|
||||
### Gaps to close — DONE
|
||||
|
||||
- ~~MCP schema tool branches (`src/basic_memory/mcp/tools/schema.py`)~~ — 18 tests in `tests/mcp/test_tool_schema.py`
|
||||
- ~~MCP schema client behavior (`src/basic_memory/mcp/clients/schema.py`)~~ — `tests/mcp/test_client_schema.py`
|
||||
- ~~Schema router error-path branches (`src/basic_memory/api/v2/routers/schema_router.py`)~~ — `tests/api/v2/test_schema_router.py`
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add MCP tool tests for `schema_validate` strict + non-strict result shapes.~~ **DONE**
|
||||
- ~~Add MCP tool tests for `schema_infer` with explicit `entity_type` and inferred type fallback.~~ **DONE**
|
||||
- ~~Add MCP tool tests for `schema_diff` empty-diff and non-empty-diff paths.~~ **DONE**
|
||||
- ~~Add API tests for schema router invalid payload/edge error handling.~~ **DONE**
|
||||
- Add integration test that starts MCP server and calls schema tools end-to-end on fixture notes. — deferred to backlog item 4.
|
||||
|
||||
### 2) Semantic Search (`0777879`, `1428d18`, `344e651`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- `search_type=text|vector|hybrid` returns expected ranked results on canonical semantic corpus.
|
||||
- Missing semantic dependencies fail fast with actionable install guidance.
|
||||
- Reindex and provider/model changes produce valid vectors without dimension mismatch.
|
||||
- SQLite and Postgres produce equivalent behavior for semantic modes on the same dataset.
|
||||
- Generated-column migration path is valid on SQLite environments in use.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/repository/test_sqlite_vector_search_repository.py`
|
||||
- `tests/repository/test_postgres_search_repository.py`
|
||||
- `tests/services/test_semantic_search.py`
|
||||
- `tests/mcp/test_tool_search.py`
|
||||
- `test-int/test_search_performance_benchmark.py`
|
||||
|
||||
### Gaps to close — DONE
|
||||
|
||||
- ~~Uncovered Postgres vector/hybrid branches~~ — 20 tests in `tests/repository/test_postgres_search_repository_unit.py` + 5 integration tests in `test-int/semantic/test_semantic_coverage.py`
|
||||
- ~~MCP search semantic/output branches~~ — expanded `tests/mcp/test_tool_search.py`
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Expand Postgres repository tests for vector query composition edge cases.~~ **DONE**
|
||||
- ~~Expand Postgres repository tests for hybrid fusion ranking and pagination branches.~~ **DONE**
|
||||
- ~~Expand Postgres repository tests for embedding/provider error handling branches.~~ **DONE**
|
||||
- ~~Expand MCP search tool tests for vector/hybrid output formatting branches.~~ **DONE**
|
||||
- ~~Expand MCP search tool tests for semantic-disabled and missing-dependency failures.~~ **DONE**
|
||||
- Add MCP integration tests that start server and execute semantic `search_notes` tool calls. — deferred to backlog item 4.
|
||||
|
||||
### Semantic search quality benchmarks (NEW)
|
||||
|
||||
Full benchmark suite in `test-int/semantic/` covering 5 backend×provider combinations:
|
||||
- `sqlite-fts`, `sqlite-fastembed`, `postgres-fts`, `postgres-fastembed`, `postgres-openai`
|
||||
- Quality metrics: hit@1, recall@5, MRR@10 with per-query timing
|
||||
- Realistic corpus with cross-topic vocabulary overlap (240 notes, 4 topics)
|
||||
- Rich CLI viewer: `just semantic-report`
|
||||
- JSON artifact output: `just test-semantic-report`
|
||||
|
||||
Key finding: **FastEmbed (384-d local ONNX) matches or exceeds OpenAI (1536-d) quality at 30x lower latency.** Recommending FastEmbed as default for both local and cloud deployments.
|
||||
|
||||
### 3) Per-Project Local/Cloud Routing + API Key Auth (`d84708c`, `ed94877`, `312662f`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Project mode (`local`/`cloud`) persists and displays correctly.
|
||||
- Routing selects ASGI for local projects and HTTP+Bearer for cloud projects.
|
||||
- Cloud project without key fails with explicit remediation (`cloud set-key`/`cloud create-key`).
|
||||
- Resolution precedence is correct (factory > force-local > per-project cloud > global fallback > local).
|
||||
- Watch/sync only run for local projects.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/mcp/test_async_client_modes.py`
|
||||
- `tests/cli/test_project_set_cloud_local.py`
|
||||
- `tests/mcp/test_project_context.py`
|
||||
- `tests/test_project_resolver.py`
|
||||
- `tests/sync/test_watch_service_reload.py`
|
||||
|
||||
### Gaps to close — DONE
|
||||
|
||||
- ~~Cloud routing branch gaps in `src/basic_memory/mcp/async_client.py`~~ — expanded `tests/mcp/test_async_client_modes.py`
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add branch-focused tests for all unresolved routing branches in `get_client()`.~~ **DONE**
|
||||
- Add MCP integration scenario with mixed local/cloud project config — deferred to backlog item 4.
|
||||
|
||||
### 4) Project-Prefixed Permalinks + Memory URL Routing (`545804f`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Project-prefixed permalinks are generated consistently on create/update/import flows.
|
||||
- Memory URLs resolve to the correct project/entity even with duplicate note titles.
|
||||
- `read_note`, `search`, `build_context`, write/edit/move flows preserve project identity correctly.
|
||||
- Link resolution remains correct for context-aware wikilinks.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/utils/test_permalink_formatting.py`
|
||||
- `tests/mcp/test_tool_read_note.py`
|
||||
- `tests/mcp/test_tool_search.py`
|
||||
- `tests/services/test_context_service.py`
|
||||
- `test-int/mcp/test_read_note_integration.py`
|
||||
|
||||
### Gaps to close
|
||||
|
||||
- No major coverage alarm in report, but keep as regression-critical due broad impact surface.
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add one integration test with colliding titles across two projects and assert URL routing invariants.~~ **DONE** — `test-int/mcp/test_permalink_collision_integration.py` (2 tests: collision across projects + memory:// URL routing with project prefix)
|
||||
|
||||
### 5) MCP UI Variants + TUI Output (`8bc03d1`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- UI resource variant selection (`tool-ui`, `vanilla`, `mcp-ui`) follows env configuration.
|
||||
- `search_notes` and `read_note` expose expected resource metadata for UI hosts.
|
||||
- `ascii`/`ansi` outputs are deterministic and stable for terminal clients.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/mcp/test_tool_contracts.py`
|
||||
- `test-int/mcp/test_output_format_json_integration.py`
|
||||
- `test-int/mcp/test_ui_sdk_integration.py`
|
||||
|
||||
### Gaps to close — DONE
|
||||
|
||||
- ~~`src/basic_memory/mcp/tools/ui_sdk.py` branch coverage~~ — `tests/mcp/test_ui_sdk.py`
|
||||
- ~~`src/basic_memory/mcp/ui/sdk.py` and `src/basic_memory/mcp/ui/templates.py` branch coverage~~ — `tests/mcp/test_ui_templates.py` + `tests/mcp/test_ui_resources.py`
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add unit tests for UI SDK metadata generation and template selection branches.~~ **DONE** — 31 tests
|
||||
- ~~Add integration assertion for variant-specific resource URIs and metadata payload shape.~~ **DONE**
|
||||
|
||||
### 6) Watch Command (`8df88e4`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- `basic-memory watch` starts and processes create/update/delete events.
|
||||
- Watch restart/reload path does not duplicate watchers.
|
||||
- Cloud-mode projects are excluded from active watcher set.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/cli/test_watch.py`
|
||||
- `tests/sync/test_coordinator.py`
|
||||
- `tests/sync/test_watch_service_reload.py`
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add one stress-style integration test for rapid file changes and watcher stability.~~ **DONE** — `tests/sync/test_watch_service_stress.py` (3 tests: 50-file batch, mixed add/modify/delete batch, rapid modifications to same file)
|
||||
|
||||
### 7) CLI JSON Output (`a47c9c0`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- `--format json` returns valid JSON with stable keys for success paths.
|
||||
- Error paths also return JSON-shaped output with correct non-zero exits.
|
||||
- Default human output remains unchanged.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/cli/test_cli_tool_json_output.py`
|
||||
- `test-int/cli/test_cli_tool_json_integration.py`
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add one failure-path integration test per high-use tool command.~~ **DONE** — `test-int/cli/test_cli_tool_json_failure_integration.py` (4 tests: read-note not found, write-note missing content, write→read roundtrip, recent-activity empty project)
|
||||
|
||||
### 8) Search/Edit and Metadata Fixes (`530cbac`, `f1d50c2`, `8838571`, `009e849`) — DONE
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Metadata filters produce consistent results on SQLite and Postgres.
|
||||
- `tag:` shorthand works alone and with mixed query terms.
|
||||
- Fast write/edit paths preserve `external_id` and metadata integrity.
|
||||
|
||||
### Existing coverage anchor points
|
||||
|
||||
- `tests/repository/test_metadata_filters.py`
|
||||
- `tests/repository/test_search_repository.py`
|
||||
- `tests/services/test_search_service.py`
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add Postgres-specific metadata filter edge-case tests to mirror SQLite assertions exactly.~~ **DONE** — `tests/repository/test_metadata_filters_edge_cases.py` (6 tests: missing field, AND logic, contains single-element array, nested path missing intermediate, $gte/$lte boundaries, $between inclusive — all pass on both SQLite and Postgres)
|
||||
|
||||
### 9) Compatibility and Hotfix Regression Pack (`c46d7a6`, `a0e754b`, `343a6e1`, `24ca5f6`, `e3ced49`, `8489a3d`, `b609c4e`, `f6e0a5b`, `7624a20`)
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Legacy endpoints required by older CLI versions function without `405` (`GET /projects/projects`, `POST /projects/projects`, `POST /projects/config/sync`).
|
||||
- Entity creation conflicts map to conflict status (not 500).
|
||||
- `recent_activity` prompt defaults are correct.
|
||||
- No spurious `metadata: {}` in serialized frontmatter.
|
||||
- Tigris/rclone uses global consistency headers for all transaction types.
|
||||
- `bm --version` fast path avoids heavy import path and remains responsive.
|
||||
- Default SQLite DB path is isolated by config dir.
|
||||
|
||||
### Gaps to close
|
||||
|
||||
- ~~Commits with no direct tests added (`c46d7a6`, `344e651`, `f6e0a5b`) need explicit regression tests.~~ **DONE**
|
||||
|
||||
### Planned additions — DONE
|
||||
|
||||
- ~~Add API compat test covering all legacy endpoint methods and payloads.~~ **DONE** — `test_legacy_v1_add_project_endpoint`, `test_legacy_v1_sync_config_endpoint`
|
||||
- ~~Add CLI fast-path test for `--version` import behavior/performance guard.~~ **DONE** — `test_bm_version_does_not_import_heavy_modules`
|
||||
- ~~Add empty metadata serialization regression test.~~ **DONE** — `test_schema_to_markdown_empty_metadata_no_metadata_key`
|
||||
- Add migration safety test for SQLite generated columns (`VIRTUAL` expectation) — deferred, low risk.
|
||||
|
||||
## MCP Manual Verification Plan (LLM Tool Calls)
|
||||
|
||||
Run after automated tests pass.
|
||||
|
||||
### Setup
|
||||
|
||||
- Start MCP server: `basic-memory mcp --transport stdio`
|
||||
- Use an MCP-capable client and issue tool calls directly.
|
||||
|
||||
### Manual scenarios
|
||||
|
||||
- Schema: call `schema_validate`, `schema_infer`, and `schema_diff` on known fixtures.
|
||||
- Schema: verify error and success payloads match acceptance criteria.
|
||||
- Semantic search: call `search_notes` with `search_type=text|vector|hybrid`.
|
||||
- Semantic search: verify ranking relevance on semantic fixture queries.
|
||||
- Routing: call tools with explicit project on mixed local/cloud setup.
|
||||
- Routing: verify success/failure paths with and without API key.
|
||||
- Permalink routing: read/write/search notes across projects with colliding titles.
|
||||
- Permalink routing: verify memory URL routing correctness.
|
||||
- UI/TUI: call `search_notes` and `read_note` with UI variants and `output_format=text|json`.
|
||||
- UI/TUI: verify payload/resource format and metadata completeness.
|
||||
|
||||
## Implementation Backlog (Ordered)
|
||||
|
||||
1. ~~Fill schema MCP/client/router coverage gaps.~~ **DONE** — 18 tests in `test_tool_schema.py` + `test_client_schema.py`
|
||||
2. ~~Fill semantic search MCP + Postgres repository gaps.~~ **DONE** — 20 tests in `test_postgres_search_repository_unit.py` + `test_tool_search.py`
|
||||
3. ~~Add compatibility regression tests (legacy endpoints, migration, version fast path).~~ **DONE** — 5 tests across 3 files (see below)
|
||||
4. ~~Add feature-level integration tests (permalinks, watch, CLI JSON, metadata filters).~~ **DONE** — 15 tests across 4 files (see items 4, 6, 7, 8 above)
|
||||
5. ~~Expand UI SDK and template branch tests.~~ **DONE** — 31 tests in `test_ui_templates.py` + `test_ui_sdk.py` + `test_ui_resources.py`
|
||||
6. ~~Run full gate and capture results in a short release readiness summary.~~ **DONE** — see results below
|
||||
|
||||
### Full Gate Results (`just check`)
|
||||
|
||||
| Phase | Result |
|
||||
|-------|--------|
|
||||
| lint | PASS |
|
||||
| format | PASS |
|
||||
| typecheck | PASS |
|
||||
| Unit tests (SQLite) | 1788 passed, 15 skipped |
|
||||
| Integration tests (SQLite) | 243 passed, 4 skipped, 10 deselected |
|
||||
| Unit tests (Postgres) | 1760 passed, 28 skipped |
|
||||
| Integration tests (Postgres) | 234 passed, 13 skipped, 10 deselected |
|
||||
|
||||
**0 failures. 10 deselected = semantic benchmark tests (run separately via `just test-semantic`).**
|
||||
|
||||
### Item 3 Details — Compatibility Regression Tests
|
||||
|
||||
| Test | File | What it covers |
|
||||
|------|------|----------------|
|
||||
| `test_legacy_v1_add_project_endpoint` | `tests/api/v2/test_project_router.py` | POST `/projects/projects` legacy route reachable (idempotent path) |
|
||||
| `test_legacy_v1_sync_config_endpoint` | `tests/api/v2/test_project_router.py` | POST `/projects/config/sync` legacy route reachable |
|
||||
| `test_bm_version_does_not_import_heavy_modules` | `tests/cli/test_cli_exit.py` | `bm --version` fast path does not load `basic_memory.mcp` |
|
||||
| `test_schema_to_markdown_empty_metadata_no_metadata_key` | `tests/markdown/test_entity_parser_error_handling.py` | `schema_to_markdown()` with `entity_metadata={}` emits no `metadata:` key |
|
||||
| `test_legacy_v1_list_projects_endpoint` | `tests/api/v2/test_project_router.py` | (pre-existing) GET `/projects/projects` legacy route |
|
||||
|
||||
**Suite totals after item 3: 1764 passed, 15 skipped, 0 failures.**
|
||||
|
||||
## Suggested Commands
|
||||
|
||||
- Full suite: `just check`
|
||||
- Fast loop: `just fast-check`
|
||||
- E2E consistency: `just doctor`
|
||||
- SQLite focused: `just test-sqlite`
|
||||
- Postgres focused: `just test-postgres`
|
||||
- Schema integration: `pytest test-int/test_schema -q`
|
||||
- Semantic + repo focus: `pytest tests/repository/test_postgres_search_repository.py tests/mcp/test_tool_search.py tests/services/test_semantic_search.py -q`
|
||||
- MCP integration focus: `pytest test-int/mcp -q`
|
||||
|
||||
## Exit Criteria for This Plan
|
||||
|
||||
- All feature acceptance criteria above are validated.
|
||||
- All identified high-priority coverage gaps are addressed or explicitly documented as intentional.
|
||||
- Manual MCP verification scenarios complete with no P0/P1 findings.
|
||||
@@ -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);
|
||||
@@ -0,0 +1,318 @@
|
||||
# v0.19.0 Release Notes
|
||||
|
||||
## Overview
|
||||
|
||||
v0.19.0 is a major release that introduces semantic vector search, a schema validation system,
|
||||
project-prefixed permalinks, per-project cloud routing, and a significant upgrade to FastMCP 3.0.
|
||||
It includes 90+ commits since v0.18.0 spanning new features, architectural improvements, and
|
||||
stability fixes across both SQLite and Postgres backends.
|
||||
|
||||
---
|
||||
|
||||
## Major Features
|
||||
|
||||
### Semantic Vector Search
|
||||
|
||||
Full vector and hybrid search for SQLite (via sqlite-vec) and Postgres (via pgvector).
|
||||
|
||||
- **Hybrid search mode** combines full-text search (FTS) with vector similarity for best results
|
||||
- **Score-based fusion** replaces RRF for hybrid ranking — `max(vec, fts) + 0.3 * min(vec, fts)` preserves dominant signals and rewards dual-source agreement (#577)
|
||||
- **Default search mode** is now `hybrid` when semantic search is enabled, `text` when disabled
|
||||
- Embedding providers: FastEmbed (local, default) or OpenAI API
|
||||
- Configurable similarity threshold via `semantic_min_similarity` (default 0.55)
|
||||
- Per-query `min_similarity` override on `search_notes` tool
|
||||
- Auto-backfill: existing entities get embeddings generated on first startup
|
||||
- Backend-specific distance-to-similarity conversion (cosine for SQLite, inner product for Postgres)
|
||||
- FTS fallback: if semantic dependencies are missing, search gracefully degrades to text-only
|
||||
- sqlite-vec knn `k` parameter capped at 4096 to prevent backend errors
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"semantic_search_enabled": true,
|
||||
"semantic_embedding_provider": "fastembed",
|
||||
"semantic_embedding_model": "bge-small-en-v1.5",
|
||||
"semantic_min_similarity": 0.55
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```
|
||||
search_notes("machine learning concepts", search_type="hybrid")
|
||||
search_notes("similar to my notes on coffee", search_type="vector")
|
||||
search_notes("exact phrase match", search_type="text")
|
||||
search_notes("broad search", min_similarity=0.3) # lower threshold for more results
|
||||
```
|
||||
|
||||
### Schema System
|
||||
|
||||
Validate note structure against user-defined schemas with frontmatter-based rules.
|
||||
|
||||
- Define schemas as YAML in note frontmatter with field types, required fields, and constraints
|
||||
- Frontmatter validation during sync — malformed notes get clear error messages
|
||||
- Schema inference from existing notes to bootstrap schemas from your content
|
||||
- Schema diff to compare two schemas and see changes
|
||||
- Available via MCP tools and CLI
|
||||
|
||||
### Project-Prefixed Permalinks
|
||||
|
||||
Permalinks now include the project name for unambiguous cross-project references.
|
||||
|
||||
- Memory URLs like `memory://project-name/folder/note` route to the correct project
|
||||
- Existing non-prefixed permalinks continue to work (backwards compatible)
|
||||
- Controlled by `permalinks_include_project` config (default: true)
|
||||
- `build_context` and `search_notes` auto-detect project from URL prefix
|
||||
|
||||
### Per-Project Cloud Routing
|
||||
|
||||
Individual projects can be routed through the cloud while others stay local.
|
||||
|
||||
- Set a project to cloud mode: `bm project set-cloud research`
|
||||
- Revert to local: `bm project set-local research`
|
||||
- Uses API key authentication: `bm cloud set-key bmc_abc123...`
|
||||
- MCP tools automatically route based on each project's mode
|
||||
- Local MCP server (`bm mcp`) still uses local routing for all projects by default
|
||||
- `--local` and `--cloud` CLI flags override per-command
|
||||
|
||||
### Workspace Selection
|
||||
|
||||
Cloud projects can target specific workspaces for multi-tenant environments.
|
||||
|
||||
- `workspace` parameter on MCP tools for explicit workspace targeting
|
||||
- CLI workspace-aware project listing with `bm project list`
|
||||
- Spinner feedback while fetching cloud projects
|
||||
|
||||
---
|
||||
|
||||
## New Tools and Capabilities
|
||||
|
||||
### Dashboard (`bm project info`)
|
||||
|
||||
`bm project info` now displays an htop-inspired compact dashboard with:
|
||||
|
||||
- Horizontal bar charts for note types (top 5)
|
||||
- Embedding coverage bar with Unicode block characters
|
||||
- Colored status dots for at-a-glance health
|
||||
- `EmbeddingStatus` schema and `get_embedding_status()` service method for programmatic access
|
||||
|
||||
### Unified Metadata Search
|
||||
|
||||
`search_by_metadata` has been merged into `search_notes` — one tool for all searches.
|
||||
`query` is now optional, so you can search purely by frontmatter metadata.
|
||||
|
||||
```
|
||||
search_notes(metadata_filters={"status": "in-progress"})
|
||||
search_notes(metadata_filters={"tags": ["security", "oauth"]})
|
||||
search_notes(metadata_filters={"priority": {"$in": ["high", "critical"]}})
|
||||
search_notes(metadata_filters={"schema.confidence": {"$gt": 0.7}})
|
||||
search_notes(tags=["security"]) # convenience shorthand
|
||||
search_notes(status="draft") # convenience shorthand
|
||||
```
|
||||
|
||||
### JSON Output Mode
|
||||
|
||||
All MCP tools now support `output_format="json"` for machine-readable responses.
|
||||
|
||||
- Default remains `"text"` for human-readable output (no breaking changes)
|
||||
- `build_context` defaults to `"json"` with slimmed payloads (redundant fields stripped)
|
||||
- CLI tool commands support `--format json` flag
|
||||
|
||||
### `tag:` Search Shorthand
|
||||
|
||||
Search by tag using convenient shorthand syntax.
|
||||
|
||||
```
|
||||
search_notes("tag:security")
|
||||
search_notes("tag:coffee AND tag:brewing")
|
||||
```
|
||||
|
||||
### Entity User Tracking
|
||||
|
||||
Entities now track `created_by` and `last_updated_by` fields for attribution.
|
||||
|
||||
### Improved Search Result Content (#609)
|
||||
|
||||
Search results now surface more relevant context:
|
||||
|
||||
- `matched_chunk_text` populated for FTS-only hybrid results (no more fallback to truncated content)
|
||||
- `TOP_CHUNKS_PER_RESULT` increased from 3 to 5, catching answers deeper in large notes (~2700 → ~4500 chars)
|
||||
- `CONTENT_DISPLAY_LIMIT` doubled from 2000 to 4000 chars for results without matched chunks
|
||||
|
||||
### `write_note` Overwrite Guard (#632)
|
||||
|
||||
`write_note` is now non-idempotent by default. If a note already exists, the tool returns an
|
||||
error instead of silently overwriting. Pass `overwrite=True` to replace, or use `edit_note`
|
||||
for incremental updates. Config option `write_note_overwrite_default` restores the old upsert
|
||||
behavior.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Changes
|
||||
|
||||
### Score-Based Hybrid Fusion (#577)
|
||||
|
||||
RRF (Reciprocal Rank Fusion) compressed all fused scores to ~0.016, destroying ranking
|
||||
differentiation. The new formula `max(vec, fts) + FUSION_BONUS * min(vec, fts)` preserves
|
||||
dominant signals and rewards dual-source agreement. Zero-score results now produce zero
|
||||
fused score instead of receiving a 0.1 weight floor.
|
||||
|
||||
### FastMCP 3.0 Upgrade
|
||||
|
||||
Upgraded from FastMCP 2.12.3 to 3.0.1.
|
||||
|
||||
- Tool annotations (`readOnlyHint`, `openWorldHint`) for better client integration
|
||||
- Improved MCP protocol compliance
|
||||
- Better error handling and context management
|
||||
|
||||
### Prompts Call MCP Tools Directly
|
||||
|
||||
MCP prompts (`search`, `continue_conversation`) now call MCP tools directly instead of
|
||||
going through API endpoints. This fixes empty results in discovery mode and ensures prompts
|
||||
use the same resolution logic as tools (including LinkResolver fallback).
|
||||
|
||||
### build_context LinkResolver Fallback
|
||||
|
||||
`build_context` now falls back to LinkResolver when an exact permalink lookup returns empty.
|
||||
This uses the same 7-strategy resolution pipeline as `read_note`, so callers no longer get
|
||||
empty results for valid note identifiers that don't match exact permalinks.
|
||||
|
||||
### Sync Handles Semantic Dependency Errors Gracefully
|
||||
|
||||
When sqlite-vec or another embedding provider is unavailable, `sync_file` now catches
|
||||
`SemanticDependenciesMissingError` separately. The entity is created and FTS-indexed
|
||||
successfully — only vector embeddings are skipped, with a clear warning:
|
||||
|
||||
```
|
||||
WARNING: Semantic search dependencies missing — vector embeddings skipped for path=note.md.
|
||||
Run 'bm reindex --embeddings' after resolving the dependency issue.
|
||||
```
|
||||
|
||||
### Unified Project Path
|
||||
|
||||
Cloud projects with bisync now store the local filesystem path in `path` (not the Docker
|
||||
container path). Config migration automatically promotes `local_sync_path` → `path` for
|
||||
existing configs.
|
||||
|
||||
---
|
||||
|
||||
## CLI Improvements
|
||||
|
||||
### Status and Doctor Default to Local Routing
|
||||
|
||||
`bm status` and `bm doctor` now default to local routing since they scan the local filesystem.
|
||||
Previously, cloud-mode projects would route these commands to the cloud API, which returned
|
||||
Docker-internal paths that don't exist locally.
|
||||
|
||||
### `--format json` for CLI Tool Commands
|
||||
|
||||
All `bm tool` subcommands support `--format json` for machine-readable output, enabling
|
||||
integration with scripts and plugins.
|
||||
|
||||
### `--json` for Top-Level CLI Commands
|
||||
|
||||
Five additional CLI commands now support `--json` for machine-readable output:
|
||||
|
||||
- `bm status --json` — sync report with new/modified/deleted/moved files and skipped files
|
||||
- `bm project list --json` — structured project list with name, paths, routing mode, and defaults
|
||||
- `bm schema validate --json` — validation report with per-note pass/fail, warnings, and errors
|
||||
- `bm schema infer --json` — field frequency analysis and suggested schema definition
|
||||
- `bm schema diff --json` — drift report with new fields, dropped fields, and cardinality changes
|
||||
|
||||
This complements the existing `bm project info --json` and `bm tool --format json` support,
|
||||
making all major CLI commands scriptable for CI pipelines and automation.
|
||||
|
||||
### Cloud Promo and Analytics
|
||||
|
||||
- Cloud promo panel shown on first run or version bump with OSS discount code
|
||||
- Anonymous usage telemetry via Umami Cloud (promo/login funnel events only)
|
||||
- Opt out with `BASIC_MEMORY_NO_PROMOS=1`
|
||||
- No PII, no file contents, no per-command tracking
|
||||
- See [Telemetry](https://github.com/basicmachines-co/basic-memory#telemetry) in README
|
||||
|
||||
---
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **#577**: RRF fusion compressed all hybrid scores to ~0.016, destroying ranking differentiation
|
||||
- **#582**: build_context returns empty results on valid note identifiers
|
||||
- **#575**: Remove hardcoded "main" default from default_project
|
||||
- **#595**: recent_activity dedup and pagination across MCP tools
|
||||
- **#593**: Backend-specific distance-to-similarity conversion
|
||||
- **#592**: Strip NUL bytes from content before PostgreSQL search indexing
|
||||
- **#562**: Use VIRTUAL instead of STORED columns in SQLite migration
|
||||
- **#558**: Add X-Tigris-Consistent headers to all rclone commands
|
||||
- **#541**: Handle EntityCreationError as conflict
|
||||
- **#536**: Stabilize metadata filters on Postgres
|
||||
- **#533**: Fix recent_activity prompt defaults
|
||||
- **#530**: Prevent spurious `metadata: {}` in frontmatter output
|
||||
- **#601**: Return matched chunk text in search results
|
||||
- **#606**: Accept `null` for `expected_replacements` in `edit_note`
|
||||
- **#579, #607**: Guard against closed streams in promo panel and missing vector tables on shutdown
|
||||
- **#609**: FTS-only hybrid results missing `matched_chunk_text`; content limits too conservative
|
||||
- **#631**: `build_context` related_results schema validation failure — replaced fragile `_slim_context()` stripping with Pydantic `exclude=True` field config
|
||||
- **#630**: Skip workspace resolution when client factory is active — prevents 401 errors in cloud MCP server mode
|
||||
- **#30**: `tag:` prefix query fails with hybrid search — moved tag prefix parsing to MCP tool level so it works with all search modes
|
||||
- **#31**: `search_notes` returns cluttered observation/relation-level results — now defaults to entity-level results
|
||||
- **#28**: `schema_infer` and `schema_diff` return raw Pydantic models as "undefined" in LLM output — added markdown formatters
|
||||
- Fix `schema_validate` identifier resolution (now uses LinkResolver) and text rendering (markdown formatter)
|
||||
- **#634**: `schema_validate` and `schema_diff` use stale database metadata instead of reading schema definitions from file — now reads frontmatter directly from the file with fallback to database metadata
|
||||
- Fix `Post(**metadata)` crash when frontmatter contains `content` or `handler` keys
|
||||
- Fix list-valued frontmatter fields (`title`, `type`) crashing on `.strip()` — now coerced to strings
|
||||
- Cap sqlite-vec knn `k` parameter at 4096 to prevent backend errors
|
||||
- Parameterize SQL queries in search repository type filters
|
||||
- Double-default display in project list
|
||||
- `ensure_frontmatter_on_sync` default changed to `True`
|
||||
- Status/doctor commands fail with cloud-mode projects (Docker path error)
|
||||
- Prompts return "0 projects" in discovery mode
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
- Upgrade `cryptography` for CVE advisory
|
||||
- Upgrade `python-multipart` for security advisory
|
||||
|
||||
---
|
||||
|
||||
## Internal / Developer
|
||||
|
||||
- **#598**: Upgrade FastMCP 2.12.3 → 3.0.1 with tool annotations
|
||||
- **#594**: Add `ty` as supplemental type checker
|
||||
- **#538**: Add fast feedback loop tooling (`just fast-check`, `just doctor`, `just testmon`)
|
||||
- **#600**: Rename `entity_type` to `note_type` for consistency
|
||||
- **#596**: Fix CLI runtime defects and audit regressions
|
||||
- CLI refactoring and workspace-aware cloud project listing
|
||||
- Split and speed up PR test matrix in CI
|
||||
- Fix CI: collect coverage from test jobs instead of re-running all tests
|
||||
- Create `search_vector_chunks` in test fixtures for Postgres compatibility
|
||||
|
||||
---
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
| Setting | Old Default | New Default | Notes |
|
||||
|---------|-------------|-------------|-------|
|
||||
| `semantic_search_enabled` | `false` | `true` | Semantic search on by default |
|
||||
| `ensure_frontmatter_on_sync` | `false` | `true` | Frontmatter added during sync |
|
||||
| `permalinks_include_project` | `false` | `true` | Project prefix in permalinks |
|
||||
|
||||
---
|
||||
|
||||
## Upgrade Notes
|
||||
|
||||
- **Semantic search dependencies** are now included by default. If sqlite-vec fails to load,
|
||||
search gracefully falls back to FTS. Run `bm reindex --embeddings` to generate embeddings
|
||||
for existing content.
|
||||
- **Hybrid search scoring** has changed from RRF to score-based fusion. Search result ordering
|
||||
may differ — results should be more accurate with better score differentiation.
|
||||
- **`search_by_metadata`** is removed as a standalone tool. Use `search_notes` with
|
||||
`metadata_filters` instead (same parameters, same behavior).
|
||||
- **Project-prefixed permalinks** are enabled by default. Existing notes keep their current
|
||||
permalinks until modified. Set `permalinks_include_project: false` to disable.
|
||||
- **Frontmatter on sync** is now enabled by default. Files without frontmatter will have it
|
||||
added on next sync. Set `ensure_frontmatter_on_sync: false` to preserve old behavior.
|
||||
- **Config migration** runs automatically for cloud projects with bisync — `local_sync_path`
|
||||
is promoted to `path` so filesystem operations work correctly.
|
||||
- **`write_note` is no longer idempotent** — calls to `write_note` for existing notes now
|
||||
return an error unless `overwrite=True` is passed. Use `edit_note` for incremental changes,
|
||||
or set `write_note_overwrite_default: true` in config to restore the old behavior.
|
||||
@@ -0,0 +1,209 @@
|
||||
# Semantic Search Manual Test Log
|
||||
|
||||
## Overview
|
||||
|
||||
Manual test session for semantic (vector) search on the main project.
|
||||
- Date: 2026-02-15
|
||||
- Database: ~/.basic-memory/memory.db (SQLite)
|
||||
- Entities: 456 embedded, 2714 vector chunks
|
||||
- Search index: 2390 FTS entries
|
||||
- Embedding model: default (384-dim, sqlite-vec)
|
||||
|
||||
## Test Plan
|
||||
|
||||
1. **Search Type Routing** — verify vector/hybrid/text dispatch, invalid search_type handling
|
||||
2. **Conceptual Queries** — natural language where vector should beat FTS
|
||||
3. **Keyword Queries** — exact terms where FTS should be strong
|
||||
4. **Hybrid Ranking** — queries where both FTS and vector contribute
|
||||
5. **Result Types** — entities, observations, relations in vector results
|
||||
6. **Filters + Vector** — combine vector with types/entity_types/after_date
|
||||
7. **Edge Cases** — short queries, long queries, empty, special chars, no-match
|
||||
8. **Pagination** — page > 1, page_size respected
|
||||
|
||||
---
|
||||
|
||||
## Test Results
|
||||
|
||||
### Test 1: Search Type Routing
|
||||
|
||||
#### 1a: search_type="semantic" (invalid value)
|
||||
- **Input:** query="how does the knowledge graph work", search_type="semantic"
|
||||
- **Expected:** error or explicit fallback
|
||||
- **Actual:** Silently falls through to text search (else branch in search.py:430)
|
||||
- **Verdict:** BUG — should either be a recognized alias for "vector" or return an error
|
||||
|
||||
#### 1b: search_type="vector"
|
||||
- **Input:** query="keeping AI context between sessions", search_type="vector"
|
||||
- **Actual:** 5 results, scores ~0.58-0.59, found "Maintaining context across conversation boundaries" observation
|
||||
- **Verdict:** PASS
|
||||
|
||||
#### 1c: search_type="text" with conceptual query
|
||||
- **Input:** query="keeping AI context between sessions", search_type="text"
|
||||
- **Actual:** 0 results (no exact keyword match)
|
||||
- **Verdict:** PASS (expected — FTS requires token overlap)
|
||||
|
||||
#### 1d: search_type="hybrid" with conceptual query
|
||||
- **Input:** query="keeping AI context between sessions", search_type="hybrid"
|
||||
- **Actual:** 5 results, same ranking as vector (FTS contributed nothing here)
|
||||
- **Verdict:** PASS
|
||||
|
||||
#### 1e: search_type="text" with keyword query
|
||||
- **Input:** query="OAuth authentication", search_type="text"
|
||||
- **Actual:** 3 results — AUTH.md Supabase OAuth, OAuth Rip-and-Replace, OAuth Integration Analysis
|
||||
- **Verdict:** PASS
|
||||
|
||||
#### 1f: search_type="vector" with keyword query
|
||||
- **Input:** query="OAuth authentication", search_type="vector"
|
||||
- **Actual:** Same top results as text (keyword-rich content also scores well in vector space)
|
||||
- **Verdict:** PASS
|
||||
|
||||
---
|
||||
|
||||
### Test 2: Conceptual Queries (vector advantage)
|
||||
|
||||
#### 2a: Natural language question
|
||||
- **Input:** query="why do AI assistants forget things", search_type="vector"
|
||||
- **Actual:** 5 results — Manual Testing Session, "Balance security and usability" observation, "Tools should match thought patterns" observation. Scores ~0.56-0.57
|
||||
- **Vector advantage:** Found conceptually related content despite no exact keyword overlap
|
||||
- **Verdict:** PASS
|
||||
|
||||
#### 2b: Same query, text search
|
||||
- **Input:** query="why do AI assistants forget things", search_type="text"
|
||||
- **Actual:** 1 result — "What is Basic Memory?" (likely matched on "AI" token)
|
||||
- **Verdict:** PASS (demonstrates vector advantage — text barely matched)
|
||||
|
||||
#### 2c: Domain concept with no jargon
|
||||
- **Input:** query="pricing strategy for cloud product", search_type="vector"
|
||||
- **Actual:** 3 results — SPEC-16 MCP Cloud Service Consolidation, knowledge architecture observation, Visual Knowledge Spaces relation. Scores ~0.56-0.57
|
||||
- **Verdict:** PASS (found cloud-related content conceptually)
|
||||
|
||||
#### 2d: Technical concept, long query
|
||||
- **Input:** query="SQLite performance optimization WAL mode concurrent writes", search_type="vector"
|
||||
- **Actual:** 3 results — SPEC-11 API Performance Optimization, Real-Time Updates with WebSockets, marketing status update. Scores ~0.55-0.58
|
||||
- **Verdict:** PASS (found performance-related content)
|
||||
|
||||
---
|
||||
|
||||
### Test 3: Keyword Queries (FTS strength)
|
||||
|
||||
#### 3a: Exact term match — "OAuth authentication"
|
||||
- **Text:** 3 results with high relevance (exact matches in titles)
|
||||
- **Vector:** Same top results (keyword overlap helps vector too)
|
||||
- **Verdict:** PASS — FTS and vector converge on keyword-rich queries
|
||||
|
||||
#### 3b: "OAuth" single keyword, hybrid mode
|
||||
- **Input:** query="OAuth", search_type="hybrid"
|
||||
- **Actual:** 5 results — Basic Memory Coding Guide, AI Collaboration Examples, SPEC-18, daily note, Manual Testing Session. FTS + vector blended. Scores ~0.016-0.032
|
||||
- **Note:** Top hybrid result is "Basic Memory Coding Guide" not an OAuth-specific doc — suggests hybrid scoring may dilute strong FTS matches
|
||||
- **Verdict:** PASS but hybrid ranking questionable for single-keyword queries
|
||||
|
||||
---
|
||||
|
||||
### Test 4: Hybrid Ranking
|
||||
|
||||
#### 4a: Hybrid vs vector on "OAuth authentication"
|
||||
- **Hybrid with entity_types=["entity"]:** 5 results — RLS Implementation Lessons, Cloud Readiness Assessment, AUTH.md OAuth, Core Service Implementation, OAuth Rip-and-Replace. Scores ~0.016-0.023
|
||||
- **Vector with entity_types=["entity"]:** 5 results — Core Service Implementation, SPEC-13 CLI Auth, Coding Guide, Authentication Service, ADR Production Auth. Scores ~0.55-0.60
|
||||
- **Observation:** Hybrid surfaces different top results than vector-only. Hybrid found RLS and Cloud Readiness docs that vector didn't prioritize. Different ranking is expected from RRF fusion.
|
||||
- **Verdict:** PASS — hybrid produces meaningfully different ranking
|
||||
|
||||
---
|
||||
|
||||
### Test 5: Result Types
|
||||
|
||||
#### 5a: Vector returns all result types
|
||||
- **Input:** query="keeping AI context between sessions", search_type="vector"
|
||||
- **Entities:** SPEC-18 AI Memory Management Tool (type=entity)
|
||||
- **Relations:** Prompt Builder integrates_with (type=relation)
|
||||
- **Observations:** "Translation layer is key" (type=observation), "Maintaining context across conversation boundaries" (type=observation)
|
||||
- **Verdict:** PASS — all three types appear in vector results
|
||||
|
||||
#### 5b: Observations carry metadata
|
||||
- **Observation result:** category="challenge", content="Maintaining context across conversation boundaries", from_entity="research/ai-knowledge-management-research"
|
||||
- **Verdict:** PASS — category, content, from_entity, tags all present
|
||||
|
||||
#### 5c: Relations carry link info
|
||||
- **Relation result:** relation_type="integrates_with", from_entity="development/features/prompt-builder...", to_entity (present but truncated in some)
|
||||
- **Verdict:** PASS — relation metadata present
|
||||
|
||||
---
|
||||
|
||||
### Test 6: Filters + Vector Search
|
||||
|
||||
#### 6a: entity_types=["entity"] with vector
|
||||
- **Input:** query="OAuth authentication", search_type="vector", entity_types=["entity"]
|
||||
- **Actual:** 5 results, all type="entity" (Core Service Implementation, SPEC-13, Coding Guide, Authentication Service, ADR Auth)
|
||||
- **Verdict:** PASS — filter correctly restricts to entities only
|
||||
|
||||
#### 6b: types=["note"] with vector
|
||||
- **Input:** query="OAuth authentication", search_type="vector", types=["note"]
|
||||
- **Actual:** Same 5 results (all have entity_type="note" in metadata)
|
||||
- **Verdict:** PASS — types filter works with vector search
|
||||
|
||||
#### 6c: after_date with vector
|
||||
- **Input:** query="OAuth authentication", search_type="vector", after_date="2025-06-01"
|
||||
- **Actual:** 3 results — Core Service Implementation, Cloud Web App analysis observation, SPEC-13. Filtered out older OAuth docs.
|
||||
- **Verdict:** PASS — date filter applied correctly
|
||||
|
||||
#### 6d: entity_types=["entity"] with hybrid
|
||||
- **Input:** query="OAuth authentication", search_type="hybrid", entity_types=["entity"]
|
||||
- **Actual:** 5 results, all type="entity" — RLS lessons, Cloud Readiness, AUTH.md OAuth, Core Service, OAuth Rip-and-Replace
|
||||
- **Verdict:** PASS — filter works with hybrid mode too
|
||||
|
||||
#### 6e: types=["entity"] with vector (WRONG filter name)
|
||||
- **Input:** query="OAuth authentication", search_type="vector", types=["entity"]
|
||||
- **Actual:** 0 results
|
||||
- **Note:** `types` filters by entity_type metadata (e.g., "note", "person"), NOT by SearchItemType. Using types=["entity"] looks for entity_type="entity" which few/no notes have. This is a UX confusion point — the param names are ambiguous.
|
||||
- **Verdict:** PASS (correct behavior) but USABILITY ISSUE — easy to confuse types vs entity_types
|
||||
|
||||
---
|
||||
|
||||
### Test 7: Edge Cases
|
||||
|
||||
#### 7a: Single character query
|
||||
- **Input:** query="x", search_type="vector"
|
||||
- **Actual:** 3 results — "Self-contained application bundle" observation, Non-Markdown File Support relation, quick-win-tools entity. Scores ~0.57-0.59
|
||||
- **Note:** Single character still produces an embedding and returns results. Quality is low/random as expected.
|
||||
- **Verdict:** PASS (no crash, returns results)
|
||||
|
||||
#### 7b: Whitespace-only query
|
||||
- **Input:** query=" ", search_type="vector"
|
||||
- **Actual:** 0 results
|
||||
- **Verdict:** PASS (handled gracefully — _check_vector_eligible strips and rejects empty)
|
||||
|
||||
#### 7c: Query with no relevant content
|
||||
- **Input:** query="quantum computing blockchain", search_type="vector"
|
||||
- **Actual:** 3 results — Inter-Agent Communication relation, Self-contained bundle observation, JSON-LD interop observation. Scores ~0.54
|
||||
- **Note:** Still returns results because vector search always finds nearest neighbors. Scores are lower (~0.54) than relevant queries (~0.58-0.60). No relevance threshold applied.
|
||||
- **Verdict:** PASS (expected behavior) but NOTE — no relevance cutoff means irrelevant queries always return something
|
||||
|
||||
---
|
||||
|
||||
### Test 8: Pagination
|
||||
|
||||
#### 8a: Vector search page 2
|
||||
- **Input:** query="keeping AI context between sessions", search_type="vector", page=2, page_size=3
|
||||
- **Actual:** 3 results on page 2, current_page=2. Different results from page 1. Top: "Maintaining context across conversation boundaries" observation (score 0.587)
|
||||
- **Note:** Interestingly, page 2 had a higher-scoring result than some page 1 results. This may indicate pagination doesn't sort globally — it might be paginating within a pre-scored set.
|
||||
- **Verdict:** PASS (pagination works) but POSSIBLE ISSUE — result ordering across pages needs investigation
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### Passing Tests: 20/21
|
||||
|
||||
### Bugs Found
|
||||
1. **search_type="semantic" silently falls through** (Test 1a) — Invalid search_type values fall to the `else` branch and default to text search without any warning. Should either alias "semantic" to "vector" or raise an error.
|
||||
|
||||
### Usability Issues
|
||||
2. **types vs entity_types confusion** (Test 6e) — `types` filters by entity_type metadata (note, person, etc.) while `entity_types` filters by SearchItemType (entity, observation, relation). The naming is ambiguous and easy to mix up.
|
||||
3. **No relevance threshold** (Test 7c) — Vector search always returns nearest neighbors even for completely irrelevant queries. Consider adding a minimum score threshold or at least documenting expected score ranges.
|
||||
4. **Hybrid ranking for single keywords** (Test 3b) — Hybrid mode on simple keyword queries produced less intuitive rankings than pure FTS or pure vector. The RRF fusion may dilute strong FTS signals.
|
||||
|
||||
### Observations
|
||||
- Vector search successfully finds conceptually related content that FTS misses entirely
|
||||
- Score ranges: relevant queries ~0.56-0.60, irrelevant queries ~0.54 (narrow spread)
|
||||
- All three result types (entity, observation, relation) appear correctly in vector results
|
||||
- Filters (entity_types, types, after_date) all work correctly with vector and hybrid modes
|
||||
- Pagination works but cross-page ordering may need investigation
|
||||
@@ -0,0 +1,270 @@
|
||||
# Semantic Search
|
||||
|
||||
This guide covers Basic Memory's semantic (vector) search feature, which adds meaning-based retrieval alongside the existing full-text search.
|
||||
|
||||
## Overview
|
||||
|
||||
Basic Memory's search supports both full-text search (FTS) and semantic retrieval. Semantic search adds vector embeddings that capture the *meaning* of your content, enabling:
|
||||
|
||||
- **Paraphrase matching**: Find "authentication flow" when searching for "login process"
|
||||
- **Conceptual queries**: Search for "ways to improve performance" and find notes about caching, indexing, and optimization
|
||||
- **Hybrid retrieval**: Combine the precision of keyword search with the recall of semantic similarity
|
||||
|
||||
Semantic search is enabled by default when semantic dependencies are available at runtime. It works on both SQLite (local) and Postgres (cloud) backends.
|
||||
|
||||
## Installation
|
||||
|
||||
Semantic search dependencies (fastembed, sqlite-vec, openai) are included in the default `basic-memory` install.
|
||||
|
||||
```bash
|
||||
pip install basic-memory
|
||||
```
|
||||
|
||||
You can always override with `BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true|false`.
|
||||
|
||||
### Platform Compatibility
|
||||
|
||||
| Platform | FastEmbed (local) | OpenAI (API) |
|
||||
|---|---|---|
|
||||
| macOS ARM64 (Apple Silicon) | Yes | Yes |
|
||||
| macOS x86_64 (Intel Mac) | No — see workaround below | Yes |
|
||||
| Linux x86_64 | Yes | Yes |
|
||||
| Linux ARM64 | Yes | Yes |
|
||||
| Windows x86_64 | Yes | Yes |
|
||||
|
||||
#### Intel Mac Workaround
|
||||
|
||||
The default install includes FastEmbed, which depends on ONNX Runtime. ONNX Runtime dropped Intel Mac (x86_64) wheels starting in v1.24, so install with a compatible ONNX Runtime pin first:
|
||||
|
||||
```bash
|
||||
pip install basic-memory 'onnxruntime<1.24'
|
||||
```
|
||||
|
||||
After installation, Intel Mac users have two runtime options:
|
||||
|
||||
**Option 1: Use OpenAI embeddings (recommended)**
|
||||
|
||||
```bash
|
||||
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
|
||||
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=openai
|
||||
export OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
**Option 2: Use FastEmbed locally**
|
||||
|
||||
Keep the same pinned installation and use FastEmbed (default provider):
|
||||
|
||||
```bash
|
||||
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
|
||||
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=fastembed
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Install Basic Memory:
|
||||
|
||||
```bash
|
||||
pip install basic-memory
|
||||
```
|
||||
|
||||
2. (Optional) Explicitly enable semantic search:
|
||||
|
||||
```bash
|
||||
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
|
||||
```
|
||||
|
||||
3. Build vector embeddings for your existing content:
|
||||
|
||||
```bash
|
||||
bm reindex --embeddings
|
||||
```
|
||||
|
||||
4. Search using semantic modes:
|
||||
|
||||
```python
|
||||
# Pure vector similarity
|
||||
search_notes("login process", search_type="vector")
|
||||
|
||||
# Hybrid: combines FTS precision with vector recall (recommended)
|
||||
search_notes("login process", search_type="hybrid")
|
||||
|
||||
# Explicit full-text search
|
||||
search_notes("login process", search_type="text")
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
All settings are fields on `BasicMemoryConfig` and can be set via environment variables (prefixed with `BASIC_MEMORY_`).
|
||||
|
||||
| Config Field | Env Var | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `semantic_search_enabled` | `BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED` | Auto (`true` when semantic deps are available) | Enable semantic search. Required before vector/hybrid modes work. |
|
||||
| `semantic_embedding_provider` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER` | `"fastembed"` | Embedding provider: `"fastembed"` (local) or `"openai"` (API). |
|
||||
| `semantic_embedding_model` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_MODEL` | `"bge-small-en-v1.5"` | Model identifier. Auto-adjusted per provider if left at default. |
|
||||
| `semantic_embedding_dimensions` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_DIMENSIONS` | Auto-detected | Vector dimensions. 384 for FastEmbed, 1536 for OpenAI. Override only if using a non-default model. |
|
||||
| `semantic_embedding_batch_size` | `BASIC_MEMORY_SEMANTIC_EMBEDDING_BATCH_SIZE` | `64` | Number of texts to embed per batch. |
|
||||
| `semantic_vector_k` | `BASIC_MEMORY_SEMANTIC_VECTOR_K` | `100` | Candidate count for vector nearest-neighbour retrieval. Higher values improve recall at the cost of latency. |
|
||||
|
||||
## Embedding Providers
|
||||
|
||||
### FastEmbed (default)
|
||||
|
||||
FastEmbed runs entirely locally using ONNX models — no API key, no network calls, no cost.
|
||||
|
||||
- **Model**: `BAAI/bge-small-en-v1.5`
|
||||
- **Dimensions**: 384
|
||||
- **Tradeoff**: Smaller model, fast inference, good quality for most use cases
|
||||
|
||||
```bash
|
||||
# Install basic-memory and enable semantic search
|
||||
pip install basic-memory
|
||||
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
|
||||
```
|
||||
|
||||
### OpenAI
|
||||
|
||||
Uses OpenAI's embeddings API for higher-dimensional vectors. Requires an API key.
|
||||
|
||||
- **Model**: `text-embedding-3-small`
|
||||
- **Dimensions**: 1536
|
||||
- **Tradeoff**: Higher quality embeddings, requires API calls and an OpenAI key
|
||||
|
||||
```bash
|
||||
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
|
||||
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=openai
|
||||
export OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
When switching from FastEmbed to OpenAI (or vice versa), you must rebuild embeddings since the vector dimensions differ:
|
||||
|
||||
```bash
|
||||
bm reindex --embeddings
|
||||
```
|
||||
|
||||
## Search Modes
|
||||
|
||||
### `text` (default)
|
||||
|
||||
Full-text keyword search using FTS5 (SQLite) or tsvector (Postgres). Supports boolean operators (`AND`, `OR`, `NOT`), phrase matching, and prefix wildcards.
|
||||
|
||||
```python
|
||||
search_notes("project AND planning", search_type="text")
|
||||
```
|
||||
|
||||
This is the existing default and does not require semantic search to be enabled.
|
||||
|
||||
### `vector`
|
||||
|
||||
Pure semantic similarity search. Embeds your query and finds the nearest content vectors. Good for conceptual or paraphrase queries where exact keywords may not appear in the content.
|
||||
|
||||
```python
|
||||
search_notes("how to speed up the app", search_type="vector")
|
||||
```
|
||||
|
||||
Returns results ranked by cosine similarity. Individual observations and relations surface as first-class results, not collapsed into parent entities.
|
||||
|
||||
### `hybrid`
|
||||
|
||||
Combines FTS and vector results using score-based fusion. This is generally the best mode when you want both keyword precision and semantic recall.
|
||||
|
||||
```python
|
||||
search_notes("authentication security", search_type="hybrid")
|
||||
```
|
||||
|
||||
Score-based fusion uses the formula `max(vec, fts) + bonus * min(vec, fts)` to preserve the dominant signal while rewarding results found by both methods.
|
||||
|
||||
### When to Use Which
|
||||
|
||||
| Mode | Best For |
|
||||
|---|---|
|
||||
| `text` | Exact keyword matching, boolean queries, tag/category searches |
|
||||
| `vector` | Conceptual queries, paraphrase matching, exploratory searches |
|
||||
| `hybrid` | General-purpose search combining precision and recall |
|
||||
|
||||
## The Reindex Command
|
||||
|
||||
The `bm reindex` command rebuilds search indexes without dropping the database.
|
||||
|
||||
```bash
|
||||
# Rebuild everything (FTS + embeddings if semantic is enabled)
|
||||
bm reindex
|
||||
|
||||
# Only rebuild vector embeddings
|
||||
bm reindex --embeddings
|
||||
|
||||
# Only rebuild the full-text search index
|
||||
bm reindex --search
|
||||
|
||||
# Target a specific project
|
||||
bm reindex -p my-project
|
||||
```
|
||||
|
||||
### When You Need to Reindex
|
||||
|
||||
- **Upgrade note**: Migration now performs a one-time automatic embedding backfill on upgrade.
|
||||
- **Manual enable case**: If you explicitly had `semantic_search_enabled=false` and then turn it on
|
||||
- **Provider change**: After switching between `fastembed` and `openai`
|
||||
- **Model change**: After changing `semantic_embedding_model`
|
||||
- **Dimension change**: After changing `semantic_embedding_dimensions`
|
||||
|
||||
The reindex command shows progress with embedded/skipped/error counts:
|
||||
|
||||
```
|
||||
Project: main
|
||||
Building vector embeddings...
|
||||
✓ Embeddings complete: 142 entities embedded, 0 skipped, 0 errors
|
||||
|
||||
Reindex complete!
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Chunking
|
||||
|
||||
Each entity in the search index is split into semantic chunks before embedding:
|
||||
|
||||
- **Headers**: Markdown headers (`#`, `##`, etc.) start new chunks
|
||||
- **Bullets**: Each bullet item (`-`, `*`) becomes its own chunk for granular fact retrieval
|
||||
- **Prose sections**: Non-bullet text is merged up to ~900 characters per chunk
|
||||
- **Long sections**: Oversized content is split with ~120 character overlap to preserve context at boundaries
|
||||
|
||||
Each search index item type (entity, observation, relation) is chunked independently, so observations and relations are embeddable as discrete facts.
|
||||
|
||||
### Deduplication
|
||||
|
||||
Each chunk has a `source_hash` (SHA-256 of the chunk text). On re-sync, unchanged chunks skip re-embedding entirely. This makes incremental updates fast — only modified content triggers API calls or model inference.
|
||||
|
||||
### Hybrid Fusion
|
||||
|
||||
Hybrid search uses score-based fusion to merge FTS and vector results:
|
||||
|
||||
1. Run FTS search to get keyword-ranked results; normalize scores to [0, 1]
|
||||
2. Run vector search to get similarity-ranked results (already [0, 1])
|
||||
3. For each result, compute: `fused = max(vec_score, fts_score) + 0.3 * min(vec_score, fts_score)`
|
||||
4. Sort by fused score
|
||||
|
||||
The dominant signal (whichever source scored higher) is preserved, and dual-source agreement adds a bonus. Unlike rank-based fusion, this approach retains score magnitude — a strong vector match stays strong even without an FTS hit.
|
||||
|
||||
### Observation-Level Results
|
||||
|
||||
Vector and hybrid modes return individual observations and relations as first-class search results, not just parent entities. This means a search for "water temperature for brewing" can surface the specific observation about 205°F without returning the entire "Coffee Brewing Methods" entity.
|
||||
|
||||
## Database Backends
|
||||
|
||||
### SQLite (local)
|
||||
|
||||
- **Vector storage**: [sqlite-vec](https://github.com/asg017/sqlite-vec) virtual table
|
||||
- **Table creation**: At runtime when semantic search is first used — no migration needed
|
||||
- **Embedding table**: `search_vector_embeddings` using `vec0(embedding float[N])` where N is the configured dimensions
|
||||
- **Chunk metadata**: `search_vector_chunks` table stores chunk text, keys, and source hashes
|
||||
|
||||
The sqlite-vec extension is loaded per-connection. Vector tables are created lazily on first use.
|
||||
|
||||
### Postgres (cloud)
|
||||
|
||||
- **Vector storage**: [pgvector](https://github.com/pgvector/pgvector) with HNSW indexing
|
||||
- **Chunk metadata table**: Created via Alembic migration (`search_vector_chunks` with `BIGSERIAL` primary key)
|
||||
- **Embedding table**: `search_vector_embeddings` created at runtime (dimension-dependent, same pattern as SQLite)
|
||||
- **Index**: HNSW index on the embedding column for fast approximate nearest-neighbour queries
|
||||
|
||||
The Alembic migration creates the dimension-independent chunks table. The embeddings table and HNSW index are deferred to runtime because they depend on the configured vector dimensions.
|
||||
@@ -0,0 +1,594 @@
|
||||
# SPEC-LOCAL-GRAPH-INTELLIGENCE-IMPLEMENTATION-PLAN
|
||||
|
||||
**Status:** Draft (Decision-Complete)
|
||||
**Date:** 2026-03-05
|
||||
**Owner:** Basic Memory Engineering
|
||||
**Implementation Status (2026-03-05):** Phase 1 contract skeleton implemented in `basic-memory` branch `codex/graph-intelligence-phase1`.
|
||||
**Related Specs:**
|
||||
1. `/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE-MASTER.md`
|
||||
2. `/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE-TECHNICAL-ADDENDUM.md`
|
||||
3. `/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE.md`
|
||||
|
||||
## Scope and Intent
|
||||
|
||||
This document is the execution handoff for Local+ Graph Intelligence.
|
||||
|
||||
It defines exactly how we will deliver graph and FCM capabilities inside the existing Basic Memory architecture:
|
||||
1. FastAPI-first business logic.
|
||||
2. MCP and CLI as thin facades.
|
||||
3. Local/cloud contract parity.
|
||||
4. Tight build-test-iterate loop for fast delivery.
|
||||
|
||||
This file is intentionally implementation-oriented and does not duplicate pricing narrative from the master spec.
|
||||
|
||||
## Architecture Alignment (FastAPI-first, MCP/CLI facade)
|
||||
|
||||
Locked architecture alignment for implementation:
|
||||
1. MCP tools remain thin proxy facades.
|
||||
2. CLI `bm tool` commands call MCP tools in JSON mode.
|
||||
3. Core logic lives in FastAPI routers and services.
|
||||
4. Cloud and local share the same REST contracts.
|
||||
5. Per-project routing continues through existing project client patterns.
|
||||
|
||||
Execution mapping:
|
||||
1. API routers define public contracts in `/graph` and `/fcm` domains.
|
||||
2. Services own traversal, scoring, simulation, and fallback logic.
|
||||
3. Repositories and index providers own data access and graph index operations.
|
||||
4. MCP typed clients call REST endpoints and return JSON-first tool output.
|
||||
5. CLI passthrough executes tool calls and prints machine-friendly JSON.
|
||||
|
||||
## Locked Decisions
|
||||
|
||||
1. SQLite remains operational source for entities, relations, embeddings, and project state.
|
||||
2. Markdown remains source of truth.
|
||||
3. Oxigraph/pyoxigraph is the derived graph index for deep traversal.
|
||||
4. FCM simulation runs in Python service layer; it is not delegated to graph DB query engines.
|
||||
5. Graph index is rebuildable and disposable; stale index never blocks user workflows.
|
||||
6. FCM model and scenario artifacts persist in app database.
|
||||
7. Local+ features are gated by config flags first; entitlement wiring follows later.
|
||||
8. Graph-first vertical slices ship before deep FCM expansion.
|
||||
9. Atomic tools ship first; orchestration workflows are deferred.
|
||||
10. Existing `build_context` and `search_notes` remain backward compatible with no breaking change.
|
||||
|
||||
## Progress Snapshot (as of 2026-03-05)
|
||||
|
||||
Completed in Phase 1:
|
||||
1. Added `/graph` and `/fcm` v2 routers with all required contract endpoints.
|
||||
2. Added graph/FCM request and response schemas for all public API contracts.
|
||||
3. Added service-layer implementations for graph and FCM contract endpoints.
|
||||
4. Added typed MCP clients for graph and FCM API calls.
|
||||
5. Added MCP tools: `graph_lineage`, `graph_impact`, `graph_health`, `graph_reindex`, `fcm_simulate`, `fcm_rank_actions`, `fcm_import_model`, `fcm_export_model`.
|
||||
6. Added CLI passthrough commands under `bm tool ...` for all planned graph/FCM operations.
|
||||
7. Added scheduler task names for graph lifecycle: `sync_graph_entity`, `sync_graph_project`, `reindex_graph_project`.
|
||||
8. Added focused tests for API, MCP clients/tools, and CLI graph/FCM passthrough.
|
||||
9. Added fast-loop `just` targets: `test-graph-intel-api`, `test-graph-intel-mcp`, `test-graph-intel-cli`, `test-graph-intel`.
|
||||
|
||||
Validation completed:
|
||||
1. `just test-graph-intel` passes.
|
||||
2. `ruff check` passes on changed files.
|
||||
3. `pyright` passes on changed files.
|
||||
|
||||
Still pending after Phase 1:
|
||||
1. SQL-backed traversal/scoring for graph `lineage`, `impact`, and `health`.
|
||||
2. Oxigraph provider integration and stale-index catch-up flow.
|
||||
3. Persistent FCM model/scenario state and interop round-trip guarantees.
|
||||
4. Config-flag and entitlement gating at API/tool boundaries.
|
||||
5. Performance instrumentation and p95 envelope enforcement.
|
||||
|
||||
## Delivery Phases
|
||||
|
||||
### Phase 1: Contract skeleton
|
||||
|
||||
Status: Completed (2026-03-05)
|
||||
|
||||
Deliverables:
|
||||
1. Add `/graph` and `/fcm` API routers with request/response schemas.
|
||||
2. Add typed MCP clients for graph and FCM endpoints.
|
||||
3. Add MCP tool passthrough commands for all new operations.
|
||||
4. Add CLI `bm tool` passthrough commands mirroring MCP surface.
|
||||
5. Add minimal smoke tests for route reachability and schema validation.
|
||||
|
||||
Exit criteria:
|
||||
1. All endpoints return structured success and error envelopes.
|
||||
2. MCP/CLI paths execute end-to-end with stubbed service responses.
|
||||
|
||||
### Phase 2: Graph capabilities on SQL-backed logic
|
||||
|
||||
Status: Next active phase
|
||||
|
||||
Deliverables:
|
||||
1. Implement `lineage`, `impact`, and `health` in service layer using SQL-backed traversal and scoring.
|
||||
2. Add provenance/evidence linking in graph outputs.
|
||||
3. Add deterministic graph-health calculations for fixed snapshots.
|
||||
|
||||
Exit criteria:
|
||||
1. `graph_lineage`, `graph_impact`, and `graph_health` pass contract tests.
|
||||
2. SQL fallback behavior is explicit and covered by tests.
|
||||
|
||||
### Phase 3: Oxigraph derived index provider
|
||||
|
||||
Status: Planned
|
||||
|
||||
Deliverables:
|
||||
1. Introduce Oxigraph provider behind graph-query interface.
|
||||
2. Add lazy catch-up jobs and project-wide reindex operation.
|
||||
3. Preserve SQL fallback when index is missing or stale.
|
||||
|
||||
Exit criteria:
|
||||
1. Stale index path serves results via SQL and schedules catch-up.
|
||||
2. Index rebuild can be triggered and completed without data loss.
|
||||
|
||||
### Phase 4: FCM import/simulate/rank/export
|
||||
|
||||
Status: Planned (contract endpoints complete, full behavior pending)
|
||||
|
||||
Deliverables:
|
||||
1. Implement CSV-first import/export contracts.
|
||||
2. Implement deterministic simulation core with convergence metadata.
|
||||
3. Implement action ranking with evidence references and confidence output.
|
||||
4. Persist scenario inputs and result artifacts.
|
||||
|
||||
Exit criteria:
|
||||
1. Research flow scenario passes: import -> simulate -> rank -> export.
|
||||
2. Interop round-trip preserves node/edge counts and signed weights.
|
||||
|
||||
### Phase 5: Hardening
|
||||
|
||||
Status: Planned
|
||||
|
||||
Deliverables:
|
||||
1. Performance tuning against published latency envelopes.
|
||||
2. Local/cloud parity tests for semantics and error behavior.
|
||||
3. MCP prompt/docs updates for new graph and FCM tools.
|
||||
4. Operational docs for reindex, fallback, and troubleshooting.
|
||||
|
||||
Exit criteria:
|
||||
1. `just check` passes before merge.
|
||||
2. Acceptance criteria in this document are fully met.
|
||||
|
||||
## API and Interface Additions
|
||||
|
||||
### Shared API conventions
|
||||
|
||||
1. All endpoints are project-scoped under `/v2/projects/{project_id}`.
|
||||
2. Request and response bodies are JSON-first and agent-friendly.
|
||||
3. Success envelope is endpoint-specific payload with deterministic fields and optional probabilistic fields.
|
||||
4. Error envelope:
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "INVALID_ARGUMENT|NOT_FOUND|INDEX_NOT_READY|MODEL_INVALID|RESOURCE_LIMIT_EXCEEDED|INTERNAL_ERROR",
|
||||
"message": "string",
|
||||
"details": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
5. Latency and scale targets are p95 targets for local default hardware profile.
|
||||
|
||||
### 1) `POST /v2/projects/{project_id}/graph/lineage`
|
||||
|
||||
Purpose: explain decision lineage and supporting evidence paths.
|
||||
|
||||
Request schema:
|
||||
```json
|
||||
{
|
||||
"start": "string",
|
||||
"goal": "string|null",
|
||||
"max_hops": 4,
|
||||
"relation_filters": ["string"]
|
||||
}
|
||||
```
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"root": {"id": "string", "title": "string", "permalink": "string"},
|
||||
"paths": [
|
||||
{
|
||||
"path_id": "string",
|
||||
"nodes": [{"id": "string", "title": "string"}],
|
||||
"edges": [{"relation": "string", "direction": "outgoing|incoming"}],
|
||||
"deterministic_path_score": 0.0,
|
||||
"confidence": 0.0,
|
||||
"evidence_refs": ["memory://..."]
|
||||
}
|
||||
],
|
||||
"generated_at": "RFC3339"
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: `root`, `paths.nodes`, `paths.edges`, `deterministic_path_score`, `generated_at`.
|
||||
Probabilistic fields: `confidence`.
|
||||
Latency target: p95 <= 450ms with `max_hops<=4`.
|
||||
Scale envelope: up to 50k nodes and 300k edges.
|
||||
|
||||
### 2) `POST /v2/projects/{project_id}/graph/impact`
|
||||
|
||||
Purpose: preview impact radius before edits or decisions.
|
||||
|
||||
Request schema:
|
||||
```json
|
||||
{
|
||||
"target": "string",
|
||||
"horizon": 2,
|
||||
"relation_filters": ["string"],
|
||||
"include_reasons": true
|
||||
}
|
||||
```
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"target": {"id": "string", "title": "string"},
|
||||
"affected": [
|
||||
{
|
||||
"id": "string",
|
||||
"title": "string",
|
||||
"distance": 1,
|
||||
"impact_score": 0.0,
|
||||
"confidence": 0.0,
|
||||
"reasons": ["string"],
|
||||
"evidence_refs": ["memory://..."]
|
||||
}
|
||||
],
|
||||
"summary": {"total_considered": 0, "total_returned": 0}
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: membership, distance, summary counts.
|
||||
Probabilistic fields: `impact_score`, `confidence`.
|
||||
Latency target: p95 <= 650ms for `horizon<=3`.
|
||||
Scale envelope: default 200 results, hard cap 1000 with pagination token.
|
||||
|
||||
### 3) `GET /v2/projects/{project_id}/graph/health`
|
||||
|
||||
Purpose: report deterministic graph quality and actionable issues.
|
||||
|
||||
Query params:
|
||||
1. `scope` optional directory prefix.
|
||||
2. `timeframe` optional window like `30d`.
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"metrics": {
|
||||
"orphan_rate": 0.0,
|
||||
"stale_central_nodes": 0,
|
||||
"overloaded_hubs": 0,
|
||||
"contradiction_candidates": 0
|
||||
},
|
||||
"issues": [
|
||||
{
|
||||
"issue_type": "orphan|stale_central|overloaded_hub|contradiction_candidate",
|
||||
"entity_id": "string",
|
||||
"severity": "low|medium|high",
|
||||
"reason": "string",
|
||||
"suggested_action": "string",
|
||||
"confidence": 0.0
|
||||
}
|
||||
],
|
||||
"computed_at": "RFC3339"
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: `metrics`, issue membership for fixed snapshot.
|
||||
Probabilistic fields: contradiction confidence when applicable.
|
||||
Latency target: p95 <= 1500ms project-wide, <= 700ms scoped.
|
||||
|
||||
### 4) `POST /v2/projects/{project_id}/graph/reindex`
|
||||
|
||||
Purpose: force project-wide graph index rebuild.
|
||||
|
||||
Request schema:
|
||||
```json
|
||||
{
|
||||
"mode": "full|incremental",
|
||||
"reason": "string|null"
|
||||
}
|
||||
```
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"job_id": "string",
|
||||
"status": "queued|running|completed|failed",
|
||||
"scheduled_at": "RFC3339"
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: job metadata and status transitions.
|
||||
Probabilistic fields: none.
|
||||
Latency target: enqueue response p95 <= 120ms.
|
||||
|
||||
### 5) `POST /v2/projects/{project_id}/fcm/simulate`
|
||||
|
||||
Purpose: run FCM scenario simulation.
|
||||
|
||||
Request schema:
|
||||
```json
|
||||
{
|
||||
"actions": [{"node_id": "string", "delta": 0.2}],
|
||||
"scenario": {
|
||||
"steps": 12,
|
||||
"activation": "tanh|sigmoid|bounded_linear",
|
||||
"decay": 0.05
|
||||
},
|
||||
"clamp_rules": [{"node_id": "string", "min": -1.0, "max": 1.0}]
|
||||
}
|
||||
```
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"baseline": [{"node_id": "string", "state": 0.0}],
|
||||
"projected": [{"node_id": "string", "state": 0.0}],
|
||||
"deltas": [{"node_id": "string", "delta": 0.0}],
|
||||
"stability": {"converged": true, "iterations_used": 0, "residual": 0.0},
|
||||
"confidence": 0.0,
|
||||
"explanations": [{"node_id": "string", "top_influencers": [{"source": "string", "weight": 0.0}]}],
|
||||
"evidence_refs": ["memory://..."]
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: baseline, projected, deltas, stability for fixed model and params.
|
||||
Probabilistic fields: confidence.
|
||||
Latency target: p95 <= 1000ms for <=500 nodes and <=5000 edges.
|
||||
|
||||
### 6) `POST /v2/projects/{project_id}/fcm/rank-actions`
|
||||
|
||||
Purpose: rank candidate interventions by expected outcome and risk.
|
||||
|
||||
Request schema:
|
||||
```json
|
||||
{
|
||||
"goal": "string",
|
||||
"constraints": {
|
||||
"max_negative_impact": 0.25,
|
||||
"required_tags": ["string"],
|
||||
"disallowed_nodes": ["string"]
|
||||
},
|
||||
"top_k": 10
|
||||
}
|
||||
```
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"goal": {"node_id": "string", "label": "string"},
|
||||
"recommendations": [
|
||||
{
|
||||
"action_node_id": "string",
|
||||
"expected_goal_delta": 0.0,
|
||||
"risk_penalty": 0.0,
|
||||
"net_score": 0.0,
|
||||
"confidence": 0.0,
|
||||
"rationale": ["string"],
|
||||
"evidence_refs": ["memory://..."]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: candidate set and constraint compliance.
|
||||
Probabilistic fields: expected delta, penalty, net score, confidence.
|
||||
Latency target: p95 <= 1500ms for top-10 from <=100 candidates.
|
||||
|
||||
### 7) `POST /v2/projects/{project_id}/fcm/import`
|
||||
|
||||
Purpose: import FCM model from CSV-first contract.
|
||||
|
||||
Request schema:
|
||||
```json
|
||||
{
|
||||
"source": "string",
|
||||
"format": "csv_bundle_v1",
|
||||
"merge_mode": "replace|upsert"
|
||||
}
|
||||
```
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"import_id": "string",
|
||||
"nodes_loaded": 0,
|
||||
"edges_loaded": 0,
|
||||
"warnings": ["string"],
|
||||
"errors": ["string"]
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: counts and validation diagnostics.
|
||||
Probabilistic fields: none.
|
||||
Latency target: p95 <= 2500ms for 10k edges import.
|
||||
|
||||
### 8) `POST /v2/projects/{project_id}/fcm/export`
|
||||
|
||||
Purpose: export FCM model for interoperability.
|
||||
|
||||
Request schema:
|
||||
```json
|
||||
{
|
||||
"format": "csv_bundle_v1",
|
||||
"selection": {
|
||||
"scope": "all|tag|subgraph",
|
||||
"tag": "string|null",
|
||||
"seed_nodes": ["string"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response schema:
|
||||
```json
|
||||
{
|
||||
"export_id": "string",
|
||||
"format": "csv_bundle_v1",
|
||||
"files": [{"name": "nodes.csv", "path": "string"}, {"name": "edges.csv", "path": "string"}],
|
||||
"node_count": 0,
|
||||
"edge_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
Deterministic fields: file names and counts for fixed selection.
|
||||
Probabilistic fields: none.
|
||||
Latency target: p95 <= 1800ms for 50k edges export.
|
||||
|
||||
## Data Model and Storage Boundaries
|
||||
|
||||
1. SQLite is mandatory operational source for entities, relations, embeddings, and project metadata.
|
||||
2. Oxigraph stores derived knowledge graph index only.
|
||||
3. FCM state persists in app database with scenario artifacts and run history.
|
||||
4. Graph index is rebuildable and disposable by design.
|
||||
5. Markdown files remain canonical source of truth.
|
||||
|
||||
Implementation data boundaries:
|
||||
1. Knowledge graph schema tracks descriptive nodes and typed edges plus provenance.
|
||||
2. FCM schema tracks signed weighted causal edges and node states.
|
||||
3. Provenance model requires `evidence_refs`, `confidence`, and `updated_at`.
|
||||
4. Scenario model stores interventions, constraints, run parameters, and output deltas.
|
||||
5. Interop schema starts with CSV-first Mental Modeler contract.
|
||||
|
||||
## Background Jobs and Index Lifecycle
|
||||
|
||||
Scheduler tasks to add:
|
||||
1. `sync_graph_entity`
|
||||
2. `sync_graph_project`
|
||||
3. `reindex_graph_project`
|
||||
|
||||
Lifecycle rules:
|
||||
1. Note writes, edits, moves, and deletes schedule graph-index sync tasks.
|
||||
2. Scheduling pattern mirrors existing vector sync behavior.
|
||||
3. On stale or missing graph index, request path serves via SQL fallback and schedules catch-up.
|
||||
4. Reindex is idempotent and safe to rerun.
|
||||
5. Index version metadata is tracked per project for staleness checks.
|
||||
|
||||
Operational behaviors:
|
||||
1. Foreground requests never block on full reindex completion.
|
||||
2. Background job failures surface in health endpoints with actionable status.
|
||||
3. Reindex job can run incremental or full mode.
|
||||
4. Phase 1 note: scheduler task names and reindex enqueue path are implemented; write/edit/move/delete sync hooks still need explicit wiring.
|
||||
|
||||
## MCP and CLI Surface
|
||||
|
||||
New MCP tools:
|
||||
1. `graph_lineage`
|
||||
2. `graph_impact`
|
||||
3. `graph_health`
|
||||
4. `fcm_simulate`
|
||||
5. `fcm_rank_actions`
|
||||
6. `fcm_import_model`
|
||||
7. `fcm_export_model`
|
||||
|
||||
CLI passthrough additions:
|
||||
1. `bm tool graph-lineage ...`
|
||||
2. `bm tool graph-impact ...`
|
||||
3. `bm tool graph-health ...`
|
||||
4. `bm tool fcm-simulate ...`
|
||||
5. `bm tool fcm-rank-actions ...`
|
||||
6. `bm tool fcm-import-model ...`
|
||||
7. `bm tool fcm-export-model ...`
|
||||
|
||||
Output conventions:
|
||||
1. Default output is JSON for MCP and CLI.
|
||||
2. MCP supports optional `output_format="text"` for human-readable summaries.
|
||||
3. CLI remains JSON-first to keep agent integration deterministic.
|
||||
|
||||
## Test Strategy (fast loop + gates)
|
||||
|
||||
### Slice-by-slice loop
|
||||
|
||||
For each vertical slice, implement in this order:
|
||||
1. API contract and schema.
|
||||
2. Typed MCP client.
|
||||
3. MCP tool passthrough.
|
||||
4. CLI passthrough.
|
||||
5. Focused tests for API/MCP/CLI.
|
||||
|
||||
Fast checks per slice:
|
||||
1. Targeted `pytest` for changed API, MCP, and CLI modules.
|
||||
2. `just fast-check`.
|
||||
3. `just doctor`.
|
||||
4. `just test-graph-intel` for graph/FCM-only iteration loop.
|
||||
|
||||
Milestone gates:
|
||||
1. SQLite unit and integration pass first.
|
||||
2. Selective Postgres parity tests for new graph and FCM contracts.
|
||||
3. Full `just check` before merge.
|
||||
|
||||
### Required test cases and scenarios
|
||||
|
||||
1. Casual user impact preview before note edit.
|
||||
2. Decision audit: lineage plus evidence references explain recommendation.
|
||||
3. Graph health deterministic output for fixed snapshot.
|
||||
4. Research flow: import model -> simulate -> rank -> export.
|
||||
5. Sparse and contradictory graph input degrades gracefully.
|
||||
6. Interop round-trip preserves node/edge counts and signed weights.
|
||||
7. Local and cloud parity on contract semantics and error model.
|
||||
8. Stale index fallback path returns valid response and schedules catch-up.
|
||||
|
||||
## Rollout and Feature Flagging
|
||||
|
||||
Rollout controls:
|
||||
1. Gate graph and FCM endpoints behind config flags first.
|
||||
2. Add entitlement enforcement after behavior and reliability stabilize.
|
||||
3. Keep existing tools and endpoints fully backward compatible.
|
||||
|
||||
Suggested flags:
|
||||
1. `feature_graph_intelligence_enabled`
|
||||
2. `feature_fcm_enabled`
|
||||
3. `feature_graph_oxigraph_provider_enabled`
|
||||
4. `feature_graph_sql_fallback_enabled`
|
||||
|
||||
Rollout sequence:
|
||||
1. Enable contract skeleton in dev.
|
||||
2. Enable graph features for internal alpha users.
|
||||
3. Enable Oxigraph provider with fallback-on by default.
|
||||
4. Enable FCM import/simulate/rank/export for research alpha users.
|
||||
5. Promote to Local+ beta when acceptance criteria are met.
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
1. Risk: graph query complexity increases p95 latency.
|
||||
Mitigation: strict query caps, fallback path, and performance budgets per endpoint.
|
||||
2. Risk: stale index produces confusing outputs.
|
||||
Mitigation: explicit staleness checks, SQL fallback, and background catch-up scheduling.
|
||||
3. Risk: FCM recommendations appear opaque.
|
||||
Mitigation: require evidence references, confidence fields, and deterministic simulation metadata.
|
||||
4. Risk: local/cloud contract drift.
|
||||
Mitigation: shared schemas, contract tests, and parity checks in CI gates.
|
||||
5. Risk: integration surface grows faster than team can validate.
|
||||
Mitigation: phase gates and vertical-slice completion before opening next phase.
|
||||
|
||||
## Improvement Backlog (Post-Phase 1)
|
||||
|
||||
1. Refactor `bm tool` graph/FCM commands into a dedicated CLI module to reduce `tool.py` size and improve maintainability.
|
||||
2. Consolidate repeated MCP text-formatting helpers for graph/FCM outputs.
|
||||
3. Replace deterministic placeholder graph behavior with SQL-backed lineage/impact/health implementations.
|
||||
4. Add explicit config/entitlement enforcement for graph/FCM endpoints and tools.
|
||||
5. Add performance telemetry and p95 reporting for graph and FCM routes.
|
||||
6. Add parity and degradation tests for stale-index fallback and contradictory/sparse inputs.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. All required sections in this document are complete with no unresolved decisions.
|
||||
2. API/interface contracts are implementation-ready with request, response, error, latency, and scale details.
|
||||
3. Architecture alignment is explicit: FastAPI logic core, MCP/CLI facades, shared local/cloud contracts.
|
||||
4. Delivery phases define concrete outputs and exit criteria.
|
||||
5. Test strategy includes tight iteration loop and milestone gates.
|
||||
6. Required scenario matrix is covered in test plan and mapped to implementation phases.
|
||||
7. Rollout plan includes feature flags and backward compatibility guarantees.
|
||||
8. An implementer can execute this plan without additional architecture clarification.
|
||||
|
||||
## Assumptions and Defaults
|
||||
|
||||
1. Config-flag gating first; entitlement wiring later.
|
||||
2. Graph-first vertical slices before deep FCM expansion.
|
||||
3. Atomic tools first; orchestration layer deferred.
|
||||
4. JSON-first contracts for agent usability.
|
||||
5. No breaking changes to existing `build_context` and `search_notes`.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
1. Implementation details unrelated to graph/FCM delivery phases in this document.
|
||||
2. Migration execution.
|
||||
3. Pricing and positioning rewrites.
|
||||
4. Cloud infrastructure changes in this phase.
|
||||
@@ -0,0 +1,782 @@
|
||||
# SPEC-LOCAL-GRAPH-INTELLIGENCE-MASTER: Local+ Graph Intelligence Blueprint
|
||||
|
||||
**Status:** Draft (Iteration 2, Decision-Complete)
|
||||
**Date:** 2026-03-05
|
||||
**Owner:** Basic Memory
|
||||
**Primary Audience:** Internal build team (Product, Engineering, GTM)
|
||||
**Current Phase (2026-03-05):** Implementation Plan Phase 1 is complete; Phase 2 (SQL-backed graph logic) is the active engineering phase.
|
||||
**Related Specs:**
|
||||
1. `/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE.md`
|
||||
2. `/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE-TECHNICAL-ADDENDUM.md`
|
||||
3. `/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE-IMPLEMENTATION-PLAN.md`
|
||||
|
||||
Reading guide:
|
||||
1. Sections 1-5 define the business and product decisions.
|
||||
2. Sections 6-10 define architecture and interface contracts.
|
||||
3. Sections 11-14 define pricing, rollout, and decision gates for execution.
|
||||
|
||||
## 1) Executive Thesis
|
||||
|
||||
Basic Memory will ship **Local+ Graph Intelligence** as a premium local capability that upgrades the product from retrieval to decision support.
|
||||
|
||||
Positioning statement:
|
||||
1. "Keep your local workflow. Add decision intelligence as complexity grows."
|
||||
2. The product sells safer decisions and explainable recommendations, not graph database mechanics.
|
||||
|
||||
Locked thesis decisions:
|
||||
1. SQLite will remain the operational core.
|
||||
2. Markdown will remain source of truth.
|
||||
3. Graph and FCM indexes will be derived and rebuildable.
|
||||
4. Oxigraph/pyoxigraph will be the v1 graph index path.
|
||||
5. FCM simulation will run in a Python service layer.
|
||||
6. SurrealDB and FalkorDB will not be core dependencies in v1 due license-roadmap mismatch.
|
||||
7. Product messaging will sell outcomes (safer decisions, explainable recommendations), not database internals.
|
||||
|
||||
## 2) Problem and Opportunity
|
||||
|
||||
Current state after v0.19:
|
||||
1. Recursive SQL traversal can retrieve connected notes but becomes expensive and noisy after a few hops.
|
||||
2. Users still do manual synthesis for impact analysis, decision lineage, and contradiction resolution.
|
||||
3. Researchers need causal reasoning and scenario modeling, not only graph navigation.
|
||||
|
||||
Opportunity:
|
||||
1. Deliver a premium local tier that materially improves decision quality while keeping data local.
|
||||
2. Create a bridge from knowledge graph navigation to causal simulation (FCM).
|
||||
3. Open a research-heavy market segment that values explainability and model interoperability.
|
||||
|
||||
Business opportunity:
|
||||
1. Add a middle tier between free OSS and cloud subscription.
|
||||
2. Preserve an upgrade path to hosted collaboration for research teams later.
|
||||
3. Differentiate Basic Memory for research-grade workflows without forcing cloud adoption.
|
||||
|
||||
## 3) User Segments and Jobs-to-be-Done
|
||||
|
||||
| Segment | Primary Job-to-be-Done | Pain Today | Value Trigger |
|
||||
|---|---|---|---|
|
||||
| Casual local builder | Avoid breaking related notes when editing | Hidden dependencies and rework | Impact preview before edits |
|
||||
| Solo technical founder | Keep architecture and decision context coherent | Context overload and drift | Decision lineage + impact radius |
|
||||
| Research user | Model and test intervention strategies | No integrated causal simulation with notes | FCM simulation + action ranking |
|
||||
| Product/research lead | Synthesize evidence quickly across many docs | Fragmented understanding | Path exploration + priority briefs |
|
||||
|
||||
## 4) Product Outcomes (not feature list)
|
||||
|
||||
Local+ Graph Intelligence will optimize for these outcomes:
|
||||
1. **Change Safety:** users catch downstream impacts before they edit.
|
||||
2. **Decision Clarity:** users can explain why an answer or recommendation was produced.
|
||||
3. **Knowledge Health:** users keep larger graphs coherent with less manual audit work.
|
||||
4. **Research Leverage:** users run scenario-level reasoning tied to explicit evidence.
|
||||
|
||||
Outcome metrics (for 30-day retained Local+ cohorts):
|
||||
1. Median time-to-understanding for complex topics decreases by at least 35% for active Local+ users.
|
||||
2. User-reported surprise side effects after note edits decrease by at least 30%.
|
||||
3. At least 60% of active Local+ users invoke graph intelligence features weekly.
|
||||
4. At least 40% of research-profile Local+ users invoke one FCM workflow weekly.
|
||||
|
||||
## 5) Feature Set v1/v1.5/v2
|
||||
|
||||
### v1 (post-v0.19 launch scope)
|
||||
|
||||
Included:
|
||||
1. Decision Lineage
|
||||
2. Impact Radius
|
||||
3. Path Explorer (guided)
|
||||
4. Graph Health (orphans, stale-central nodes, overloaded hubs)
|
||||
5. CSV FCM import/export (nodes and edges)
|
||||
6. FCM simulation for explicit action scenarios
|
||||
7. FCM action ranking with evidence-linked rationale
|
||||
|
||||
Excluded:
|
||||
1. Native Mental Modeler project format write support
|
||||
2. Team governance and shared model policy controls
|
||||
3. Cloud-only enhancements
|
||||
|
||||
### v1.5
|
||||
|
||||
Included:
|
||||
1. Contradiction Watch with reconciliation queue
|
||||
2. Priority Briefs (graph + FCM leverage summary)
|
||||
3. Stronger uncertainty propagation in FCM scoring
|
||||
4. Cloud execution optionality for heavy simulation jobs
|
||||
|
||||
### v2
|
||||
|
||||
Included:
|
||||
1. Team-shared model governance
|
||||
2. Hosted collaboration features for research teams
|
||||
3. Optional native model translators beyond CSV baseline
|
||||
|
||||
Cut line policy:
|
||||
1. If a capability cannot meet explainability requirements, it moves to v1.5+.
|
||||
2. If a capability requires cloud to function, it cannot be marked v1.
|
||||
3. If a capability cannot meet local performance envelopes, it cannot be promoted into default workflows.
|
||||
|
||||
## 6) Technical Architecture (Two-Graph Model)
|
||||
|
||||
### High-level architecture
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Markdown Files Source of Truth] --> B[Parser + Sync Pipeline]
|
||||
B --> C[SQLite Operational Store]
|
||||
B --> D[Derived Knowledge Graph Index Oxigraph]
|
||||
C --> E[Graph Intelligence Service]
|
||||
D --> E
|
||||
E --> F[Lineage Impact Path Health APIs]
|
||||
C --> G[FCM Service Python]
|
||||
D --> G
|
||||
G --> H[Simulation Ranking Interop APIs]
|
||||
```
|
||||
|
||||
### Two-graph model
|
||||
|
||||
1. **Knowledge Graph (descriptive):** notes, decisions, concepts, and typed relations.
|
||||
2. **FCM Graph (causal):** signed weighted influence links between goals, drivers, risks, and interventions.
|
||||
|
||||
### Core architectural decisions
|
||||
|
||||
1. SQLite is authoritative for entities, observations, relations, metadata, embeddings, and project state.
|
||||
2. Oxigraph is a derived index for multi-hop graph traversal and graph-pattern retrieval.
|
||||
3. FCM calculations run in Python using explicit model state and deterministic numerical steps.
|
||||
4. Local mode runs fully offline.
|
||||
5. Cloud mode can execute the same contracts via adjunct services while Neon remains system of record.
|
||||
|
||||
## 7) Backend Decision and Trade-Offs
|
||||
|
||||
### Final recommendation
|
||||
|
||||
Use this stack for v1:
|
||||
1. SQLite (existing): primary operational store.
|
||||
2. Oxigraph/pyoxigraph: derived knowledge graph index.
|
||||
3. Python FCM service: causal simulation and ranking.
|
||||
|
||||
Decision rationale:
|
||||
1. This preserves local-first UX while enabling deeper traversal and causal simulation.
|
||||
2. This avoids restrictive licensing dependencies in the core product path.
|
||||
3. This keeps a clean cloud portability path where Neon remains the hosted system of record.
|
||||
|
||||
### Trade-off matrix
|
||||
|
||||
| Option | Strengths | Risks | Decision |
|
||||
|---|---|---|---|
|
||||
| SQLite + Oxigraph + Python FCM | Local-first, permissive licensing, clear service boundaries, cloud-portable | Requires translation layer for query ergonomics | **Adopt v1** |
|
||||
| Apache AGE on Postgres | SQL+graph in one engine, good cloud-side graph semantics | Neon extension support uncertainty, weaker local/cloud parity with SQLite local baseline | Defer |
|
||||
| SurrealDB | Strong integrated multi-model experience | BSL posture conflicts with future hosted/open strategy timing | Reject for v1 core |
|
||||
| FalkorDB | Graph performance and Redis ecosystem familiarity | SSPL posture conflicts with hosted/open strategy | Reject for v1 core |
|
||||
|
||||
## 8) Public APIs / Interfaces
|
||||
|
||||
All APIs are proposed MCP tool contracts for Local+ mode.
|
||||
|
||||
### Common conventions
|
||||
|
||||
1. `project` parameter is optional and follows existing Basic Memory project routing.
|
||||
2. Deterministic fields are reproducible with identical inputs and index state.
|
||||
3. Probabilistic fields are model-derived scores and include confidence metadata.
|
||||
4. Error model uses structured codes and fail-fast behavior.
|
||||
|
||||
Shared error codes:
|
||||
1. `INVALID_ARGUMENT`
|
||||
2. `NOT_FOUND`
|
||||
3. `MODEL_INVALID`
|
||||
4. `INDEX_NOT_READY`
|
||||
5. `RESOURCE_LIMIT_EXCEEDED`
|
||||
6. `INTERNAL_ERROR`
|
||||
|
||||
---
|
||||
|
||||
### 8.1 `graph_lineage(start, goal?)`
|
||||
|
||||
**Input schema:**
|
||||
```json
|
||||
{
|
||||
"start": "string (required, permalink or memory URL)",
|
||||
"goal": "string (optional, concept or decision target)",
|
||||
"max_hops": "integer (optional, default 4, range 1-6)",
|
||||
"relation_filters": ["string"],
|
||||
"project": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output schema:**
|
||||
```json
|
||||
{
|
||||
"root": {"id": "string", "title": "string", "permalink": "string"},
|
||||
"paths": [
|
||||
{
|
||||
"path_id": "string",
|
||||
"nodes": [{"id": "string", "title": "string"}],
|
||||
"edges": [{"relation": "string", "direction": "outgoing|incoming"}],
|
||||
"deterministic_path_score": 0.0,
|
||||
"confidence": 0.0,
|
||||
"evidence_refs": ["memory://..."]
|
||||
}
|
||||
],
|
||||
"generated_at": "RFC3339"
|
||||
}
|
||||
```
|
||||
|
||||
**Deterministic fields:** root, nodes, edges, deterministic path score, generated timestamp.
|
||||
**Probabilistic fields:** confidence.
|
||||
|
||||
**Latency target:** p95 <= 450ms for `max_hops<=4`, graph envelope up to 50k nodes / 300k edges.
|
||||
|
||||
**Scale envelope:**
|
||||
1. Tested local baseline: 50k nodes, 300k edges.
|
||||
2. Expected degradation: path expansion can exceed latency target when candidate paths > 20k.
|
||||
|
||||
---
|
||||
|
||||
### 8.2 `graph_impact(target, horizon, relation_filters?)`
|
||||
|
||||
**Input schema:**
|
||||
```json
|
||||
{
|
||||
"target": "string (required)",
|
||||
"horizon": "integer (required, range 1-4)",
|
||||
"relation_filters": ["string"],
|
||||
"include_reasons": "boolean (default true)",
|
||||
"project": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output schema:**
|
||||
```json
|
||||
{
|
||||
"target": {"id": "string", "title": "string"},
|
||||
"affected": [
|
||||
{
|
||||
"id": "string",
|
||||
"title": "string",
|
||||
"distance": 2,
|
||||
"impact_score": 0.0,
|
||||
"confidence": 0.0,
|
||||
"reasons": ["string"]
|
||||
}
|
||||
],
|
||||
"summary": {"total_considered": 0, "total_returned": 0}
|
||||
}
|
||||
```
|
||||
|
||||
**Deterministic fields:** membership, distance, summary counts.
|
||||
**Probabilistic fields:** impact score, confidence.
|
||||
|
||||
**Latency target:** p95 <= 650ms for `horizon<=3` under baseline envelope.
|
||||
|
||||
**Scale envelope:**
|
||||
1. `affected` default cap: 200 items.
|
||||
2. Hard cap: 1000 items with pagination token.
|
||||
|
||||
---
|
||||
|
||||
### 8.3 `graph_health(scope?, timeframe?)`
|
||||
|
||||
**Input schema:**
|
||||
```json
|
||||
{
|
||||
"scope": "string (optional, directory prefix or project-wide)",
|
||||
"timeframe": "string (optional, e.g. 30d, 90d)",
|
||||
"project": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output schema:**
|
||||
```json
|
||||
{
|
||||
"metrics": {
|
||||
"orphan_rate": 0.0,
|
||||
"stale_central_nodes": 0,
|
||||
"overloaded_hubs": 0,
|
||||
"contradiction_candidates": 0
|
||||
},
|
||||
"issues": [
|
||||
{
|
||||
"issue_type": "orphan|stale_central|overloaded_hub|contradiction_candidate",
|
||||
"entity_id": "string",
|
||||
"severity": "low|medium|high",
|
||||
"reason": "string",
|
||||
"suggested_action": "string"
|
||||
}
|
||||
],
|
||||
"computed_at": "RFC3339"
|
||||
}
|
||||
```
|
||||
|
||||
**Deterministic fields:** metrics and issue list membership for a fixed graph snapshot.
|
||||
**Probabilistic fields:** contradiction candidate confidence when present.
|
||||
|
||||
**Latency target:** p95 <= 1500ms project-wide; <= 700ms for scoped directory mode.
|
||||
|
||||
**Scale envelope:** project-wide scans tested to 50k nodes.
|
||||
|
||||
---
|
||||
|
||||
### 8.4 `fcm_simulate(actions, scenario?, clamp_rules?)`
|
||||
|
||||
**Input schema:**
|
||||
```json
|
||||
{
|
||||
"actions": [
|
||||
{"node_id": "string", "delta": 0.2}
|
||||
],
|
||||
"scenario": {
|
||||
"steps": 12,
|
||||
"activation": "tanh|sigmoid|bounded_linear",
|
||||
"decay": 0.05
|
||||
},
|
||||
"clamp_rules": [
|
||||
{"node_id": "string", "min": -1.0, "max": 1.0}
|
||||
],
|
||||
"project": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output schema:**
|
||||
```json
|
||||
{
|
||||
"baseline": [{"node_id": "string", "state": 0.12}],
|
||||
"projected": [{"node_id": "string", "state": 0.43}],
|
||||
"deltas": [{"node_id": "string", "delta": 0.31}],
|
||||
"stability": {
|
||||
"converged": true,
|
||||
"iterations_used": 9,
|
||||
"residual": 0.002
|
||||
},
|
||||
"confidence": 0.0,
|
||||
"explanations": [
|
||||
{"node_id": "string", "top_influencers": [{"source": "string", "weight": 0.7}]}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Deterministic fields:** baseline, projected, deltas, convergence metadata for fixed model and parameters.
|
||||
**Probabilistic fields:** confidence (derived from edge confidence and evidence coverage).
|
||||
|
||||
**Latency target:** p95 <= 1000ms for up to 500 nodes / 5000 edges and <=12 steps.
|
||||
|
||||
**Scale envelope:**
|
||||
1. Soft limit: 2000 nodes / 20000 edges.
|
||||
2. Over soft limit: return `RESOURCE_LIMIT_EXCEEDED` with remediation guidance.
|
||||
|
||||
---
|
||||
|
||||
### 8.5 `fcm_rank_actions(goal, constraints?, top_k?)`
|
||||
|
||||
**Input schema:**
|
||||
```json
|
||||
{
|
||||
"goal": "string (required node_id)",
|
||||
"constraints": {
|
||||
"max_negative_impact": 0.25,
|
||||
"required_tags": ["string"],
|
||||
"disallowed_nodes": ["string"]
|
||||
},
|
||||
"top_k": "integer (default 10, range 1-25)",
|
||||
"project": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output schema:**
|
||||
```json
|
||||
{
|
||||
"goal": {"node_id": "string", "label": "string"},
|
||||
"recommendations": [
|
||||
{
|
||||
"action_node_id": "string",
|
||||
"expected_goal_delta": 0.0,
|
||||
"risk_penalty": 0.0,
|
||||
"net_score": 0.0,
|
||||
"confidence": 0.0,
|
||||
"rationale": ["string"],
|
||||
"evidence_refs": ["memory://..."]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Deterministic fields:** candidate action set, constraints compliance.
|
||||
**Probabilistic fields:** expected goal delta, risk penalty, net score, confidence.
|
||||
|
||||
**Latency target:** p95 <= 1500ms for top 10 from up to 100 candidate actions.
|
||||
|
||||
**Scale envelope:**
|
||||
1. Candidate actions hard cap: 1000.
|
||||
2. For larger sets, require pre-filtering via tags/scope.
|
||||
|
||||
---
|
||||
|
||||
### 8.6 `fcm_import_model(source, format)`
|
||||
|
||||
**Input schema:**
|
||||
```json
|
||||
{
|
||||
"source": "string (required path or URI)",
|
||||
"format": "csv_bundle_v1 (required)",
|
||||
"merge_mode": "replace|upsert (default upsert)",
|
||||
"project": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output schema:**
|
||||
```json
|
||||
{
|
||||
"import_id": "string",
|
||||
"nodes_loaded": 0,
|
||||
"edges_loaded": 0,
|
||||
"warnings": ["string"],
|
||||
"errors": ["string"]
|
||||
}
|
||||
```
|
||||
|
||||
**Deterministic fields:** load counts and validation results.
|
||||
**Probabilistic fields:** none.
|
||||
|
||||
**Latency target:** p95 <= 2500ms for 10k edges CSV bundle.
|
||||
|
||||
**Scale envelope:**
|
||||
1. Maximum CSV rows per import: 250k.
|
||||
2. Above limit returns `RESOURCE_LIMIT_EXCEEDED`.
|
||||
|
||||
---
|
||||
|
||||
### 8.7 `fcm_export_model(format, selection?)`
|
||||
|
||||
**Input schema:**
|
||||
```json
|
||||
{
|
||||
"format": "csv_bundle_v1 (required)",
|
||||
"selection": {
|
||||
"scope": "all|tag|subgraph",
|
||||
"tag": "string (optional)",
|
||||
"seed_nodes": ["string"]
|
||||
},
|
||||
"project": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output schema:**
|
||||
```json
|
||||
{
|
||||
"export_id": "string",
|
||||
"format": "csv_bundle_v1",
|
||||
"files": [
|
||||
{"name": "nodes.csv", "path": "string"},
|
||||
{"name": "edges.csv", "path": "string"}
|
||||
],
|
||||
"node_count": 0,
|
||||
"edge_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
**Deterministic fields:** file set and row counts for fixed selection.
|
||||
**Probabilistic fields:** none.
|
||||
|
||||
**Latency target:** p95 <= 1800ms for 50k edges export.
|
||||
|
||||
**Scale envelope:**
|
||||
1. Max export rows: 500k total.
|
||||
2. Pagination or scoped export required above cap.
|
||||
|
||||
## 9) Data Model and Storage Boundaries
|
||||
|
||||
### 9.1 Knowledge graph schema (descriptive)
|
||||
|
||||
`KnowledgeNode`:
|
||||
1. `id: str`
|
||||
2. `kind: note|decision|spec|concept|person|project`
|
||||
3. `title: str`
|
||||
4. `permalink: str`
|
||||
5. `tags: list[str]`
|
||||
6. `updated_at: datetime`
|
||||
|
||||
`KnowledgeEdge`:
|
||||
1. `id: str`
|
||||
2. `src_id: str`
|
||||
3. `dst_id: str`
|
||||
4. `relation: str`
|
||||
5. `directionality: directed|bidirectional`
|
||||
6. `evidence_refs: list[str]`
|
||||
7. `confidence: float [0,1]`
|
||||
8. `updated_at: datetime`
|
||||
|
||||
### 9.2 FCM schema (causal signed weighted)
|
||||
|
||||
`FCMNode`:
|
||||
1. `id: str`
|
||||
2. `label: str`
|
||||
3. `node_type: goal|driver|risk|intervention|context`
|
||||
4. `state: float [-1,1]`
|
||||
5. `clamp_min: float`
|
||||
6. `clamp_max: float`
|
||||
7. `metadata: map`
|
||||
|
||||
`FCMEdge`:
|
||||
1. `id: str`
|
||||
2. `source_id: str`
|
||||
3. `target_id: str`
|
||||
4. `weight: float [-1,1]`
|
||||
5. `confidence: float [0,1]`
|
||||
6. `time_decay: float [0,1]`
|
||||
7. `evidence_refs: list[str]`
|
||||
8. `updated_at: datetime`
|
||||
|
||||
### 9.3 Provenance model
|
||||
|
||||
`ProvenanceRecord`:
|
||||
1. `entity_id: str`
|
||||
2. `evidence_refs: list[str]`
|
||||
3. `confidence: float [0,1]`
|
||||
4. `updated_at: datetime`
|
||||
5. `source_type: extracted|user_authored|imported`
|
||||
|
||||
### 9.4 Scenario model
|
||||
|
||||
`Scenario`:
|
||||
1. `id: str`
|
||||
2. `name: str`
|
||||
3. `interventions: list[{node_id, delta}]`
|
||||
4. `constraints: list[{node_id, min, max}]`
|
||||
5. `steps: int`
|
||||
6. `activation: tanh|sigmoid|bounded_linear`
|
||||
7. `created_at: datetime`
|
||||
8. `created_by: str`
|
||||
|
||||
`ScenarioResult`:
|
||||
1. `scenario_id: str`
|
||||
2. `converged: bool`
|
||||
3. `iterations_used: int`
|
||||
4. `residual: float`
|
||||
5. `goal_deltas: list[{node_id, delta}]`
|
||||
6. `confidence: float [0,1]`
|
||||
|
||||
### 9.5 Storage boundaries
|
||||
|
||||
| Layer | System of Record | Purpose | Rebuildable |
|
||||
|---|---|---|---|
|
||||
| Markdown files | File system | Canonical knowledge content | No |
|
||||
| SQLite entities/relations/embeddings | SQLite | Operational queries and project state | Yes (from markdown + embedding pipeline) |
|
||||
| Knowledge graph triples | Oxigraph | Fast graph traversal and pattern queries | Yes |
|
||||
| FCM model and snapshots | SQLite + optional artifacts | Causal model state and scenario history | Yes (from imports and authored model definitions) |
|
||||
|
||||
## 10) Mental Modeler Interoperability
|
||||
|
||||
### v1 interoperability contract
|
||||
|
||||
Format: `csv_bundle_v1`
|
||||
1. `nodes.csv`
|
||||
2. `edges.csv`
|
||||
3. Optional `scenarios.csv`
|
||||
|
||||
`nodes.csv` required columns:
|
||||
1. `node_id`
|
||||
2. `label`
|
||||
3. `node_type`
|
||||
4. `state`
|
||||
5. `clamp_min`
|
||||
6. `clamp_max`
|
||||
|
||||
`edges.csv` required columns:
|
||||
1. `edge_id`
|
||||
2. `source_id`
|
||||
3. `target_id`
|
||||
4. `weight`
|
||||
5. `confidence`
|
||||
6. `evidence_refs` (semicolon-delimited)
|
||||
|
||||
### Import rules
|
||||
|
||||
1. Missing required columns fail with `MODEL_INVALID`.
|
||||
2. Unknown node types fail fast.
|
||||
3. Weight and confidence ranges are strictly validated.
|
||||
4. Import returns warnings for dangling evidence references.
|
||||
|
||||
### Export rules
|
||||
|
||||
1. Preserve stable IDs for round-trip compatibility.
|
||||
2. Preserve signed weights exactly.
|
||||
3. Preserve confidence values exactly.
|
||||
4. Non-portable metadata is emitted to `metadata.json` sidecar when present.
|
||||
|
||||
### Native file translators
|
||||
|
||||
1. Native project-format translation is deferred to v2.
|
||||
2. CSV remains the guaranteed compatibility baseline in v1 and v1.5.
|
||||
|
||||
## 11) Pricing and Packaging
|
||||
|
||||
### Tier structure
|
||||
|
||||
| Tier | Price Monthly | Price Annual | Beta Price (25% off) | Target Persona | Core Value |
|
||||
|---|---:|---:|---:|---|---|
|
||||
| OSS Local | $0 | $0 | $0 | Casual local users | Retrieval and memory basics |
|
||||
| Local+ Graph Intelligence | $9 | $90 | $6.75 monthly / $67.50 annual | Founders, consultants, researchers | Safer changes + explainable graph + FCM simulation |
|
||||
| Cloud Pro (current anchor) | $19 | $190 | $14.25 monthly / $142.50 annual | Users who need hosted sync and cloud workflows | Managed cloud + sync + collaboration path |
|
||||
|
||||
Pricing principles:
|
||||
1. Local+ is intentionally priced between free OSS and cloud to capture users who need deeper intelligence but not hosted sync.
|
||||
2. Cloud Pro remains the hosted convenience anchor and future collaboration path.
|
||||
3. Local+ must stand on standalone local value and cannot depend on cloud features.
|
||||
|
||||
### Feature gate mapping
|
||||
|
||||
| Capability | OSS Local | Local+ | Cloud Pro |
|
||||
|---|---|---|---|
|
||||
| Search and basic context tools | Yes | Yes | Yes |
|
||||
| Decision Lineage | No | Yes | Yes |
|
||||
| Impact Radius | No | Yes | Yes |
|
||||
| Graph Health | No | Yes | Yes |
|
||||
| FCM simulate + rank | No | Yes | Yes |
|
||||
| CSV model import/export | No | Yes | Yes |
|
||||
| Hosted collaboration controls | No | No | Future add-on |
|
||||
|
||||
### Packaging decisions
|
||||
|
||||
1. Local+ remains fully local-capable and does not require cloud auth to run.
|
||||
2. Cloud Pro remains the hosted convenience and collaboration anchor.
|
||||
3. Future hosted research add-on will layer on Cloud Pro after v2 readiness.
|
||||
|
||||
## 12) Rollout Strategy
|
||||
|
||||
### 12.1 Document production iterations (locked)
|
||||
|
||||
Iteration 1 (draft complete):
|
||||
1. Complete all 15 sections in one pass.
|
||||
2. Include v1/v1.5/v2 cut lines.
|
||||
3. Include pricing and scenario definitions.
|
||||
4. Ensure no placeholders.
|
||||
|
||||
Iteration 2 (hardening and decision lock):
|
||||
1. Resolve cross-section contradictions.
|
||||
2. Convert uncertain language to locked decisions.
|
||||
3. Add measurable acceptance criteria and risk owners.
|
||||
4. Finalize execution-ready API contracts.
|
||||
|
||||
### 12.2 Product rollout phases
|
||||
|
||||
Phase A: Foundation release (v1)
|
||||
1. Graph lineage, impact, health.
|
||||
2. CSV model import/export.
|
||||
3. FCM simulation and ranking.
|
||||
4. Advanced mode UX gating for research-grade controls.
|
||||
|
||||
Phase B: Quality and confidence (v1.5)
|
||||
1. Contradiction Watch.
|
||||
2. Priority Briefs.
|
||||
3. Improved uncertainty propagation.
|
||||
4. Confidence calibration pass using real-world model feedback.
|
||||
|
||||
Phase C: Team expansion (v2)
|
||||
1. Hosted team governance.
|
||||
2. Shared model controls.
|
||||
3. Extended translator support.
|
||||
|
||||
### 12.3 Go/No-Go release gates
|
||||
|
||||
Gate to ship v1 default workflows:
|
||||
1. p95 latency targets are met within the declared scale envelopes.
|
||||
2. Scenario round-trip fidelity tests pass for CSV import/export.
|
||||
3. Every recommendation and simulation path exposes evidence references and confidence.
|
||||
|
||||
Gate to ship v1.5:
|
||||
1. Contradiction Watch precision is acceptable for default-on use.
|
||||
2. Confidence calibration reduces false-confidence reports in user testing.
|
||||
|
||||
Gate to ship v2 team features:
|
||||
1. Clear willingness-to-pay signal from team and research buyers.
|
||||
2. Cloud execution path preserves local-cloud semantic parity for core contracts.
|
||||
|
||||
## 13) Risks, Counterarguments, and Mitigations
|
||||
|
||||
| Risk | Counterargument | Severity | Likelihood | Mitigation | Owner |
|
||||
|---|---|---|---|---|---|
|
||||
| "This is just better search" | Positioning can collapse into technical jargon | High | Medium | Lead with decision safety and explainability outcomes in product copy and onboarding | Product Lead |
|
||||
| FCM feels opaque or invented | Users distrust black-box scoring | High | Medium | Require evidence refs and confidence disclosure on every recommendation | Applied AI Lead |
|
||||
| Local performance regressions | Multi-hop and simulation can feel slow on laptops | Medium | Medium | Enforce envelopes, caps, and fail-fast limit errors with guidance | Engineering Lead |
|
||||
| Research features overwhelm casual users | UX complexity can reduce adoption | Medium | High | Default to guided flows and hide advanced controls behind explicit advanced mode | Design Lead |
|
||||
| License/roadmap conflict if backend changes | Later swap to restrictive engines creates GTM risk | High | Low | Lock permissive v1 stack and require leadership sign-off for any license-restricted dependency | Product + Legal |
|
||||
| Interop mismatch with external tools | Round-trip drift harms trust with researchers | Medium | Medium | Validate node and edge parity in import/export tests and version interop schema | Integrations Lead |
|
||||
| Pricing confusion between Local+ and Cloud Pro | Buyers may not understand which tier fits | Medium | Medium | Publish explicit tier comparison focused on local intelligence vs hosted collaboration | GTM Lead |
|
||||
|
||||
## 14) Acceptance Criteria
|
||||
|
||||
### 14.1 Document acceptance criteria
|
||||
|
||||
1. Product, technical, and pricing decisions are explicit and unambiguous.
|
||||
2. API contracts include input/output schemas, error models, deterministic versus probabilistic fields, and performance envelopes.
|
||||
3. v1/v1.5/v2 cut lines are explicit and consistent.
|
||||
4. Risk register includes severity, likelihood, owner, and mitigation.
|
||||
5. Document can be handed to implementation without additional architecture decisions.
|
||||
6. Leadership can use this document directly for pricing and positioning decisions.
|
||||
|
||||
### 14.2 Product acceptance criteria for v1 delivery
|
||||
|
||||
1. `graph_lineage`, `graph_impact`, `graph_health`, `fcm_simulate`, `fcm_rank_actions`, `fcm_import_model`, and `fcm_export_model` are available as Local+ contracts.
|
||||
2. Local mode executes all v1 contracts without cloud dependency.
|
||||
3. API p95 latency targets are met within defined scale envelopes.
|
||||
4. Every ranked or simulated output includes evidence-linked rationale and confidence.
|
||||
5. CSV round-trip preserves node count, edge count, and signed weights exactly.
|
||||
|
||||
### 14.3 Scenario test matrix (required)
|
||||
|
||||
1. **Casual local user impact check**
|
||||
Expected pass:
|
||||
`graph_impact` returns ranked affected notes with reasons before a note edit.
|
||||
|
||||
2. **Research workflow simulation**
|
||||
Expected pass:
|
||||
User imports a model bundle, runs `fcm_simulate`, and receives converged deltas and rationale.
|
||||
|
||||
3. **Decision audit traceability**
|
||||
Expected pass:
|
||||
`graph_lineage` returns path and evidence references that explain recommendation origin.
|
||||
|
||||
4. **Cloud and local parity**
|
||||
Expected pass:
|
||||
Identical query inputs return semantically equivalent outputs in local and cloud modes with local fallback behavior when cloud is unavailable.
|
||||
|
||||
5. **Sparse or contradictory graph behavior**
|
||||
Expected pass:
|
||||
System degrades gracefully with explicit uncertainty and does not fabricate high-confidence recommendations.
|
||||
|
||||
6. **Interop round-trip fidelity**
|
||||
Expected pass:
|
||||
`fcm_export_model` then `fcm_import_model` preserves node and edge counts and signed weights without mutation.
|
||||
|
||||
## 15) Appendix (license notes, terminology, examples)
|
||||
|
||||
### 15.1 License notes (verified 2026-03-05)
|
||||
|
||||
1. SurrealDB core licensing is published under BSL 1.1 with DBaaS-related restrictions in its conversion window.
|
||||
2. FalkorDB is published under SSPLv1.
|
||||
3. pyoxigraph is dual-licensed Apache-2.0 or MIT.
|
||||
4. Neon extension catalog currently does not list Apache AGE as a supported extension.
|
||||
|
||||
These notes support the v1 dependency decisions in this document.
|
||||
|
||||
### 15.2 Terminology
|
||||
|
||||
1. **Knowledge graph:** descriptive relation graph derived from markdown knowledge.
|
||||
2. **FCM:** fuzzy cognitive model with signed weighted causal edges.
|
||||
3. **Deterministic field:** reproducible output field from fixed input and fixed model/index snapshot.
|
||||
4. **Probabilistic field:** score influenced by confidence weights and model uncertainty.
|
||||
|
||||
### 15.3 Assumptions and defaults
|
||||
|
||||
1. Markdown remains source of truth.
|
||||
2. SQLite remains mandatory baseline.
|
||||
3. Graph and FCM capabilities are premium Local+ features, not OSS defaults.
|
||||
4. Research-heavy features are advanced mode, while mainstream UX stays guided.
|
||||
5. Mental Modeler interoperability starts with CSV contract first; native translator support is deferred.
|
||||
|
||||
### 15.4 Out of scope for this document
|
||||
|
||||
1. Implementation code changes.
|
||||
2. Database migrations.
|
||||
3. Cloud infrastructure edits.
|
||||
4. Full instrumentation pilot plan as the primary artifact.
|
||||
|
||||
### 15.5 External references
|
||||
|
||||
1. [SurrealDB licensing](https://surrealdb.com/license)
|
||||
2. [FalkorDB licensing](https://docs.falkordb.com/References/license.html)
|
||||
3. [pyoxigraph package and license](https://pypi.org/project/pyoxigraph/)
|
||||
4. [Neon Postgres extension catalog](https://neon.com/docs/extensions/pg-extensions)
|
||||
@@ -0,0 +1,299 @@
|
||||
# SPEC-LOCAL-GRAPH-INTELLIGENCE: Technical Addendum (Graph + FCM)
|
||||
|
||||
**Status:** Draft
|
||||
**Date:** 2026-03-05
|
||||
**Owner:** Basic Memory
|
||||
**Current Phase (2026-03-05):** Contract skeleton implementation is complete; next active phase is SQL-backed graph capabilities.
|
||||
|
||||
Related product spec:
|
||||
`/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE.md`
|
||||
|
||||
Related execution spec:
|
||||
`/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE-IMPLEMENTATION-PLAN.md`
|
||||
|
||||
## Why This Addendum Exists
|
||||
|
||||
The product spec defines user value. This addendum defines the technical shape that can deliver that value without
|
||||
breaking local-first principles.
|
||||
|
||||
This addendum also introduces a second graph layer:
|
||||
|
||||
1. Knowledge graph for relationships between notes, entities, and decisions.
|
||||
2. Fuzzy Cognitive Model (FCM) graph for weighted causal reasoning over actions and outcomes.
|
||||
|
||||
Both are derived from markdown and optional user-provided models.
|
||||
|
||||
## Strategic Reality Check
|
||||
|
||||
This is a strong idea if we stage it correctly.
|
||||
|
||||
It is not a pipe dream if we avoid one trap: building a big "graph platform" before proving users repeatedly use
|
||||
decision simulation workflows.
|
||||
|
||||
The correct strategy is:
|
||||
|
||||
1. Launch high-precision graph insights first.
|
||||
2. Add FCM scoring where it changes user behavior (not as a novelty dashboard).
|
||||
3. Expand to hosted/team workflows only after local usage proves repeat value.
|
||||
|
||||
## Constraints and Design Principles
|
||||
|
||||
1. SQLite remains the operational source for entities, observations, relations, and embeddings.
|
||||
2. Markdown remains source of truth.
|
||||
3. Graph indexes are derived, rebuildable, and disposable.
|
||||
4. Premium local mode must run fully offline.
|
||||
5. Cloud deployment should support both single-tenant and SaaS later.
|
||||
6. Avoid licenses that constrain hosted/open-source strategy.
|
||||
|
||||
## Backend Recommendation
|
||||
|
||||
### Primary Recommendation
|
||||
|
||||
Use a dual-store architecture:
|
||||
|
||||
1. SQLite (existing): operational data, metadata filters, embeddings, and most retrieval.
|
||||
2. Oxigraph/pyoxigraph (new): derived graph index for graph traversal and graph-pattern queries.
|
||||
3. Python simulation layer (new): FCM state propagation, scenario runs, and decision scoring.
|
||||
|
||||
Why this is the best fit:
|
||||
|
||||
1. Permissive licensing profile.
|
||||
2. Works locally with low footprint.
|
||||
3. Cloud-compatible as sidecar service while keeping Neon Postgres as core cloud store.
|
||||
4. Clear boundary between graph query and numeric simulation concerns.
|
||||
|
||||
### Candidate Trade-Offs
|
||||
|
||||
#### Oxigraph/pyoxigraph
|
||||
|
||||
Pros:
|
||||
|
||||
1. Lightweight local embedding.
|
||||
2. Good fit for derived-index strategy.
|
||||
3. Strong path for standards-based graph representation.
|
||||
|
||||
Cons:
|
||||
|
||||
1. SPARQL fluency is less common than SQL/Cypher.
|
||||
2. Requires a translation layer so product features are not query-language-coupled.
|
||||
|
||||
#### Apache AGE (Postgres extension)
|
||||
|
||||
Pros:
|
||||
|
||||
1. SQL + graph in one engine.
|
||||
2. Attractive for cloud-side graph operations.
|
||||
|
||||
Cons:
|
||||
|
||||
1. Neon support is uncertain for this extension.
|
||||
2. Local/cloud parity is harder if local uses SQLite.
|
||||
|
||||
#### SurrealDB / FalkorDB
|
||||
|
||||
Pros:
|
||||
|
||||
1. Strong graph-oriented developer experience.
|
||||
|
||||
Cons:
|
||||
|
||||
1. License posture is misaligned with a future hosted/open-source roadmap unless commercial terms are accepted.
|
||||
|
||||
Decision:
|
||||
Do not make these core dependencies for v1 of Local+ Graph Intelligence.
|
||||
|
||||
## Two-Graph Model
|
||||
|
||||
### A) Knowledge Graph (Descriptive)
|
||||
|
||||
Node examples:
|
||||
|
||||
1. Note
|
||||
2. Decision
|
||||
3. Spec
|
||||
4. Person
|
||||
5. Project
|
||||
6. Concept
|
||||
|
||||
Edge examples:
|
||||
|
||||
1. `depends_on`
|
||||
2. `informed_by`
|
||||
3. `contradicts`
|
||||
4. `supports`
|
||||
5. `implements`
|
||||
6. `derived_from`
|
||||
|
||||
Purpose:
|
||||
Power navigation, lineage, path explanation, impact radius, and health checks.
|
||||
|
||||
### B) FCM Graph (Causal, Signed, Weighted)
|
||||
|
||||
Node examples:
|
||||
|
||||
1. Goal: "Reduce regressions"
|
||||
2. Driver: "Test coverage"
|
||||
3. Risk: "Scope creep"
|
||||
4. Intervention: "Add review gate"
|
||||
5. Context variable: "Team bandwidth"
|
||||
|
||||
Edge attributes:
|
||||
|
||||
1. `weight` in [-1.0, 1.0]
|
||||
2. `confidence` in [0.0, 1.0]
|
||||
3. `evidence_refs` (links to notes/specs)
|
||||
4. `time_decay` (optional)
|
||||
|
||||
Purpose:
|
||||
Power scenario simulation and action ranking, not generic retrieval.
|
||||
|
||||
## Premium Feature Mapping to Architecture
|
||||
|
||||
### Decision Lineage
|
||||
|
||||
Backed by:
|
||||
|
||||
1. Knowledge graph path queries.
|
||||
2. Evidence references stored on edges.
|
||||
|
||||
### Impact Radius
|
||||
|
||||
Backed by:
|
||||
|
||||
1. Multi-hop neighborhood expansion with relation-type weights.
|
||||
2. Risk ranking using centrality + recency + confidence.
|
||||
|
||||
### Contradiction Watch
|
||||
|
||||
Backed by:
|
||||
|
||||
1. Candidate contradiction edges.
|
||||
2. Confidence-scored reconciliation queue.
|
||||
|
||||
### Priority Briefs
|
||||
|
||||
Backed by:
|
||||
|
||||
1. Health metrics (orphan rate, stale-central nodes, unresolved contradictions).
|
||||
2. Optional FCM "top leverage actions" summary.
|
||||
|
||||
### New Premium Feature: Action Simulator
|
||||
|
||||
Backed by:
|
||||
|
||||
1. FCM scenario runs over selected action nodes.
|
||||
2. Ranked interventions with expected positive/negative downstream effects.
|
||||
3. Explicit rationale graph for every recommendation.
|
||||
|
||||
## Mental Modeler Interop Plan
|
||||
|
||||
Goal:
|
||||
Make Basic Memory the AI-enabled operating layer around existing researcher workflows, not a replacement for their tools.
|
||||
|
||||
Interoperability phases:
|
||||
|
||||
1. Import/export edge lists and node tables via CSV as the baseline interchange.
|
||||
2. Preserve concept IDs and metadata so round-trips remain stable.
|
||||
3. Add translator support for native model files if/when schema contracts are validated with partner data.
|
||||
|
||||
Validation requirement:
|
||||
|
||||
1. Round-trip tests must preserve node count, edge count, and signed weights.
|
||||
2. Confidence/evidence metadata may be Basic Memory extensions and should degrade gracefully when exported.
|
||||
|
||||
## Suggested Tool/API Surface (Product-Facing)
|
||||
|
||||
1. `graph_lineage(start, goal?)`
|
||||
Returns explainable evidence paths.
|
||||
2. `graph_impact(target, horizon=2..4)`
|
||||
Returns ranked affected nodes with reasons.
|
||||
3. `graph_health()`
|
||||
Returns actionable graph quality issues.
|
||||
4. `fcm_simulate(actions, scenario?)`
|
||||
Returns projected effects and uncertainty.
|
||||
5. `fcm_rank_actions(goal, constraints?)`
|
||||
Returns top candidate actions with trade-offs.
|
||||
6. `fcm_import_model(source)` / `fcm_export_model(format)`
|
||||
Handles interop with external cognitive mapping workflows.
|
||||
|
||||
## Local and Cloud Deployment Shape
|
||||
|
||||
### Local (Primary)
|
||||
|
||||
1. SQLite + local embeddings.
|
||||
2. Oxigraph as local sidecar/index library.
|
||||
3. FCM simulation in process.
|
||||
|
||||
### Cloud (Future-Compatible)
|
||||
|
||||
1. Neon Postgres remains system of record in hosted mode.
|
||||
2. Graph index service runs per tenant or shared multi-tenant with strict tenancy boundaries.
|
||||
3. FCM simulation service can run stateless workers reading graph snapshots.
|
||||
|
||||
Principle:
|
||||
Do not require cloud to run premium local features.
|
||||
|
||||
## Rollout Plan With Go/No-Go Gates
|
||||
|
||||
### Phase 0: Proof of Utility (4-6 weeks)
|
||||
|
||||
Deliver:
|
||||
|
||||
1. Decision Lineage
|
||||
2. Impact Radius
|
||||
3. CSV FCM import + `fcm_simulate` prototype
|
||||
|
||||
Gate to continue:
|
||||
|
||||
1. Repeated weekly usage by pilot users.
|
||||
2. Users report changed decisions, not just curiosity clicks.
|
||||
|
||||
### Phase 1: Productized Local+ Beta
|
||||
|
||||
Deliver:
|
||||
|
||||
1. Graph health workflow
|
||||
2. Contradiction Watch
|
||||
3. Action ranking with explicit rationale
|
||||
|
||||
Gate to continue:
|
||||
|
||||
1. Retention of graph features after first month.
|
||||
2. Measured reduction in "surprise side effects" after edits.
|
||||
|
||||
### Phase 2: Hosted Expansion
|
||||
|
||||
Deliver:
|
||||
|
||||
1. Optional cloud execution for heavy simulations.
|
||||
2. Team-shared model governance.
|
||||
|
||||
Gate to continue:
|
||||
|
||||
1. Clear willingness to pay for hosted collaboration.
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
Risk: FCM outputs feel "made up."
|
||||
Mitigation: Require evidence links and confidence scoring in every recommendation.
|
||||
|
||||
Risk: Research-heavy feature alienates casual users.
|
||||
Mitigation: Keep FCM features in an advanced mode; default to concise guidance workflows.
|
||||
|
||||
Risk: Overengineering early graph stack.
|
||||
Mitigation: Keep derived-index architecture and strict phase gates tied to behavior change.
|
||||
|
||||
Risk: Interop friction with external tooling.
|
||||
Mitigation: Start with transparent CSV contract and strict round-trip validation.
|
||||
|
||||
## Candid Recommendation
|
||||
|
||||
Pursue this. It is a high-upside differentiation path for Local+ if executed with staged validation.
|
||||
|
||||
The key is to sell outcomes:
|
||||
|
||||
1. "Safer decisions"
|
||||
2. "Explainable recommendations"
|
||||
3. "Faster synthesis for complex research"
|
||||
|
||||
Avoid selling "graph DB" as the product. That is implementation detail.
|
||||
@@ -0,0 +1,262 @@
|
||||
# SPEC-LOCAL-GRAPH-INTELLIGENCE: Premium Local Graph Intelligence
|
||||
|
||||
**Status:** Draft
|
||||
**Date:** 2026-03-05
|
||||
**Owner:** Basic Memory
|
||||
**Current Phase (2026-03-05):** Phase 1 contract foundation shipped; engineering is now executing SQL-backed Phase 2 graph logic.
|
||||
|
||||
Companion technical addendum:
|
||||
`/docs/specs/SPEC-LOCAL-GRAPH-INTELLIGENCE-TECHNICAL-ADDENDUM.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Add a premium local feature that turns Basic Memory from "search and recall" into "explain and guide."
|
||||
|
||||
The value is not a new database. The value is better decisions for local users:
|
||||
|
||||
1. Understand why something matters.
|
||||
2. See what will be affected before making a change.
|
||||
3. Detect weak spots in the knowledge base early.
|
||||
4. Navigate complex knowledge intentionally instead of loading everything.
|
||||
|
||||
This feature is additive. Existing local workflows remain intact.
|
||||
|
||||
## Positioning
|
||||
|
||||
Core message:
|
||||
"Your notes do more than store knowledge. They reveal consequences, lineage, and blind spots."
|
||||
|
||||
Local user promise:
|
||||
|
||||
1. Keep files local.
|
||||
2. Keep markdown as source of truth.
|
||||
3. Get advanced graph intelligence as an opt-in premium capability.
|
||||
|
||||
## Problem
|
||||
|
||||
Today, deep graph navigation is possible but often expensive in context size and hard to steer for complex questions.
|
||||
Users can find information, but they still do manual synthesis to answer:
|
||||
|
||||
1. What changed because of this note?
|
||||
2. Why did we decide this?
|
||||
3. What might break if I update this?
|
||||
4. Which parts of the graph are stale, isolated, or contradictory?
|
||||
|
||||
The cost is time, cognitive load, and missed risk.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Provide clear, explainable graph insights that users can act on.
|
||||
2. Make deep navigation feel guided, not overwhelming.
|
||||
3. Help users prevent mistakes before they happen.
|
||||
4. Create premium local value that is easy to understand and justify.
|
||||
5. Keep feature behavior transparent and trustworthy.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
1. Replacing SQLite as the primary operational store.
|
||||
2. Changing markdown as source of truth.
|
||||
3. Forcing users to learn graph query languages.
|
||||
4. Building a cloud-only feature set.
|
||||
5. Turning Basic Memory into an enterprise BI product.
|
||||
|
||||
## Product Frame: From Retrieval to Reasoning
|
||||
|
||||
The feature should be framed as a shift in user outcome:
|
||||
|
||||
1. Retrieval: "Find me the note."
|
||||
2. Reasoning: "Show me the path, impact, and confidence around this note."
|
||||
|
||||
This is the main narrative upgrade for premium local users.
|
||||
|
||||
## Premium Value Pillars
|
||||
|
||||
### 1) Decision Confidence
|
||||
|
||||
Users can see decision lineage:
|
||||
|
||||
1. What evidence supported a decision.
|
||||
2. Which notes/specs informed it.
|
||||
3. How that decision evolved over time.
|
||||
|
||||
### 2) Change Safety
|
||||
|
||||
Users can run impact-aware workflows:
|
||||
|
||||
1. Estimate blast radius before editing.
|
||||
2. Surface downstream dependencies.
|
||||
3. Prioritize what to review first.
|
||||
|
||||
### 3) Knowledge Quality
|
||||
|
||||
Users can maintain graph health:
|
||||
|
||||
1. Detect orphaned notes.
|
||||
2. Detect overloaded hub notes.
|
||||
3. Detect stale but high-centrality notes.
|
||||
4. Detect likely contradictions.
|
||||
|
||||
### 4) Guided Navigation
|
||||
|
||||
Users can explore deeper relationships without context explosion:
|
||||
|
||||
1. Follow promising branches.
|
||||
2. Stop when confidence is sufficient.
|
||||
3. Avoid "load everything and hope."
|
||||
|
||||
## Feature Catalog (Value-First)
|
||||
|
||||
### A. Decision Lineage
|
||||
|
||||
What users get:
|
||||
|
||||
1. A clear "why chain" for important conclusions.
|
||||
2. Traceable connections to supporting notes.
|
||||
3. Better handoffs and historical understanding.
|
||||
|
||||
### B. Impact Radius
|
||||
|
||||
What users get:
|
||||
|
||||
1. A ranked list of likely affected notes before edits.
|
||||
2. Safer refactors for docs, plans, and architecture.
|
||||
3. Reduced accidental drift and inconsistency.
|
||||
|
||||
### C. Knowledge Health Dashboard
|
||||
|
||||
What users get:
|
||||
|
||||
1. Weekly health signals for the graph.
|
||||
2. Actionable cleanup targets.
|
||||
3. Better long-term memory quality with less manual auditing.
|
||||
|
||||
### D. Path Explorer
|
||||
|
||||
What users get:
|
||||
|
||||
1. "Show me how A connects to B" style explanations.
|
||||
2. Multiple candidate paths with confidence cues.
|
||||
3. Better discovery across large note collections.
|
||||
|
||||
### E. Contradiction Watch
|
||||
|
||||
What users get:
|
||||
|
||||
1. Early warnings for conflicting statements.
|
||||
2. Suggested reconciliation workflow.
|
||||
3. Higher trust in the knowledge base.
|
||||
|
||||
### F. Priority Briefs
|
||||
|
||||
What users get:
|
||||
|
||||
1. Periodic "what matters now" graph summaries.
|
||||
2. Focused recommendations, not noisy activity dumps.
|
||||
3. Better focus for solo builders and small teams.
|
||||
|
||||
## User Personas and Why They Pay
|
||||
|
||||
### Solo Technical Founder
|
||||
|
||||
Pain:
|
||||
Cannot hold full architecture and decision history in working memory.
|
||||
|
||||
Premium value:
|
||||
Impact Radius + Decision Lineage prevent rework and regressions.
|
||||
|
||||
### Product/Research Lead
|
||||
|
||||
Pain:
|
||||
Knowledge is fragmented across specs, notes, and decisions.
|
||||
|
||||
Premium value:
|
||||
Path Explorer + Priority Briefs compress synthesis time.
|
||||
|
||||
### Consultant/Fractional Operator
|
||||
|
||||
Pain:
|
||||
Frequent context switching across domains and clients.
|
||||
|
||||
Premium value:
|
||||
Knowledge Health + Decision Lineage speed onboarding and reporting.
|
||||
|
||||
## Packaging Direction
|
||||
|
||||
Suggested packaging:
|
||||
|
||||
1. OSS Local: existing search + context tools.
|
||||
2. Local+ Graph Intelligence: advanced graph insight features listed above.
|
||||
3. Future Team Add-On: shared policies, shared graph health views, shared lineage views.
|
||||
|
||||
Core upsell line:
|
||||
"Keep your local workflow. Add graph intelligence when complexity grows."
|
||||
|
||||
## Experience Principles
|
||||
|
||||
1. Explainability first.
|
||||
Every advanced result should show "why this was suggested."
|
||||
|
||||
2. Actionability over novelty.
|
||||
Insights should lead to concrete next steps, not abstract charts.
|
||||
|
||||
3. Progressive disclosure.
|
||||
Start with concise summaries, expand on demand.
|
||||
|
||||
4. Deterministic where possible.
|
||||
Users should trust repeated runs of the same workflow.
|
||||
|
||||
5. Respect local-first expectations.
|
||||
No surprise cloud dependency in premium local mode.
|
||||
|
||||
## Success Criteria (Product)
|
||||
|
||||
1. Users can describe the benefit in one sentence:
|
||||
"It shows me what matters and what breaks before I change things."
|
||||
2. Premium users report lower time-to-understanding for complex topics.
|
||||
3. Premium users report fewer "surprise side effects" after edits.
|
||||
4. Premium users keep larger knowledge graphs healthy with less manual effort.
|
||||
5. Feature adoption is driven by outcomes, not by curiosity-only usage.
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
Risk: Feature sounds like "just better search."
|
||||
Mitigation: Lead messaging with decision confidence and change safety, not traversal depth.
|
||||
|
||||
Risk: Feature feels too advanced for normal users.
|
||||
Mitigation: Package as guided insights and reports, not as a query language.
|
||||
|
||||
Risk: Insight quality feels noisy.
|
||||
Mitigation: Focus launch scope on high-precision insight types and transparent rationale.
|
||||
|
||||
Risk: Value is hard to prove.
|
||||
Mitigation: Track user-facing outcomes (time saved, risk avoided, cleanup completed).
|
||||
|
||||
## Rollout Narrative
|
||||
|
||||
Phase 1: "Safer Changes"
|
||||
|
||||
1. Impact Radius
|
||||
2. Decision Lineage
|
||||
|
||||
Phase 2: "Health and Clarity"
|
||||
|
||||
1. Knowledge Health Dashboard
|
||||
2. Contradiction Watch
|
||||
|
||||
Phase 3: "Strategic Navigation"
|
||||
|
||||
1. Path Explorer
|
||||
2. Priority Briefs
|
||||
|
||||
## One-Line Positioning Options
|
||||
|
||||
1. "Local notes, strategic intelligence."
|
||||
2. "Know what changed, why it matters, and what it affects."
|
||||
3. "From note-taking to decision support."
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Which two features best define the paid tier at launch?
|
||||
2. Which insight types should be guaranteed deterministic in v1?
|
||||
3. Should Priority Briefs be bundled or separate as an add-on?
|
||||
4. What is the simplest in-product education flow for first-time premium users?
|
||||
@@ -0,0 +1,225 @@
|
||||
# SPEC-LOCAL-PLUS-PUBLISH: Local+ Published Notes and Privacy Tiers
|
||||
|
||||
**Status:** Draft
|
||||
**Date:** 2026-02-14
|
||||
**Owner:** Basic Memory
|
||||
|
||||
## Summary
|
||||
|
||||
Add a paid Local+ feature that lets users publish selected notes to shareable URLs while keeping the
|
||||
main knowledge base local-first. Use this as a product wedge for users who do not want full cloud
|
||||
hosting but do want collaboration and distribution features.
|
||||
|
||||
This spec also captures a practical position on "zero knowledge" for Local+.
|
||||
|
||||
## Context
|
||||
|
||||
Basic Memory already has strong local-first primitives and optional cloud routing/sync. A recurring
|
||||
request is:
|
||||
|
||||
- keep knowledge local by default,
|
||||
- pay for selective value-add,
|
||||
- share specific outputs externally.
|
||||
|
||||
Published Notes fits this model: explicit per-note opt-in, reversible, and easy to understand.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Provide an Obsidian Publish-style sharing experience for selected notes.
|
||||
2. Keep local markdown files as source of truth.
|
||||
3. Make sharing compatible with current cloud/auth/billing primitives.
|
||||
4. Define clear Local+ packaging that does not degrade OSS local workflows.
|
||||
5. Document zero-knowledge constraints so product decisions are explicit.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
1. Full hosted editing for all notes (Cloud Full remains separate).
|
||||
2. Public website builder/CMS features.
|
||||
3. Strict cryptographic zero-knowledge server processing for MCP/search in v1.
|
||||
|
||||
## Local+ Feature Catalog (Sellable)
|
||||
|
||||
Core Local+ candidates:
|
||||
|
||||
1. Published Notes (share URL, revoke, expiry, password).
|
||||
2. Snapshot Time Machine (point-in-time restore for local projects).
|
||||
3. Recovery Drill Reports (automated restore verification).
|
||||
4. Device/API Key Governance (per-device keys, revocation, audit trail).
|
||||
5. BYO Storage Orchestration (managed setup for user-owned object storage).
|
||||
6. Semantic Boost Add-on (higher quality retrieval options while files remain source-of-truth).
|
||||
|
||||
Team-oriented add-ons:
|
||||
|
||||
1. Team-owned shared links and domain branding.
|
||||
2. Role-based publish permissions.
|
||||
3. Shared workspace policies for what can be published.
|
||||
|
||||
## Proposed MVP: Published Notes
|
||||
|
||||
### User Experience
|
||||
|
||||
Per note actions:
|
||||
|
||||
1. Publish.
|
||||
2. Unpublish.
|
||||
3. Copy URL.
|
||||
4. Regenerate URL.
|
||||
5. Set visibility and controls.
|
||||
|
||||
Controls:
|
||||
|
||||
1. Visibility: `unlisted` (default) or `public`.
|
||||
2. Optional password gate.
|
||||
3. Optional expiration datetime.
|
||||
4. Optional "disable indexing" flag for public mode.
|
||||
|
||||
Behavior:
|
||||
|
||||
1. Source note remains local markdown.
|
||||
2. Publish is explicit opt-in per note.
|
||||
3. Unpublish removes public access immediately.
|
||||
4. Republish creates a new URL token unless user chooses to keep current URL.
|
||||
|
||||
### URL Model
|
||||
|
||||
1. Unlisted share URL: high-entropy token path.
|
||||
2. Public URL: slug path (optional, later phase).
|
||||
3. Team plans can support custom domain mapping in later phase.
|
||||
|
||||
### Content Model
|
||||
|
||||
v1 published page includes:
|
||||
|
||||
1. Rendered markdown body.
|
||||
2. Optional metadata (title, updated_at).
|
||||
|
||||
v1 excludes:
|
||||
|
||||
1. Full graph traversal expansion.
|
||||
2. Related note auto-discovery on public pages.
|
||||
|
||||
### Sync Model
|
||||
|
||||
1. Local file remains canonical.
|
||||
2. Publish stores a rendered snapshot plus metadata in cloud.
|
||||
3. Update path:
|
||||
- manual "update published version", or
|
||||
- optional auto-update on note change (plan-gated).
|
||||
|
||||
## Architecture (v1)
|
||||
|
||||
### High-Level Flow
|
||||
|
||||
1. Client selects a note to publish.
|
||||
2. Client sends publish request with note identifier and policy.
|
||||
3. Service resolves note content (local sync artifact or explicit upload payload).
|
||||
4. Service stores published artifact and returns share URL.
|
||||
|
||||
### Data Model
|
||||
|
||||
`published_notes`
|
||||
|
||||
1. `id` (uuid)
|
||||
2. `tenant_id` or `workspace_id`
|
||||
3. `project_id`
|
||||
4. `entity_permalink` (or stable external_id)
|
||||
5. `share_token` (hashed in DB)
|
||||
6. `visibility` (`unlisted`|`public`)
|
||||
7. `password_hash` (nullable)
|
||||
8. `expires_at` (nullable)
|
||||
9. `is_active`
|
||||
10. `published_content` (rendered snapshot or reference)
|
||||
11. `published_at`
|
||||
12. `updated_at`
|
||||
|
||||
### API Shape (Draft)
|
||||
|
||||
1. `POST /api/published-notes`
|
||||
2. `GET /api/published-notes`
|
||||
3. `GET /api/published-notes/{id}`
|
||||
4. `PATCH /api/published-notes/{id}`
|
||||
5. `DELETE /api/published-notes/{id}` (unpublish)
|
||||
6. `POST /api/published-notes/{id}/regenerate-url`
|
||||
7. `GET /p/{token}` (public resolver)
|
||||
|
||||
### CLI Shape (Draft)
|
||||
|
||||
1. `bm cloud publish <identifier>`
|
||||
2. `bm cloud publish list`
|
||||
3. `bm cloud publish update <id>`
|
||||
4. `bm cloud publish unpublish <id>`
|
||||
5. `bm cloud publish rotate-url <id>`
|
||||
|
||||
### Security
|
||||
|
||||
1. Default to unlisted URLs.
|
||||
2. Store only hashed share tokens.
|
||||
3. Passwords hashed server-side.
|
||||
4. Enforce expiration at request time.
|
||||
5. Log publish/unpublish/rotate events for auditability.
|
||||
|
||||
## Packaging and Pricing Direction
|
||||
|
||||
Suggested split:
|
||||
|
||||
1. OSS Local: no publish URLs.
|
||||
2. Local+ Solo: publish URLs + snapshots + recovery.
|
||||
3. Local+ Team: solo features + team governance and branding.
|
||||
4. Cloud Full: hosted app + full cloud workflows.
|
||||
|
||||
Key message:
|
||||
"Keep everything local. Publish only what you choose."
|
||||
|
||||
## Rollout Plan
|
||||
|
||||
1. Phase 1: Unlisted publish URLs + unpublish + regenerate URL.
|
||||
2. Phase 2: Password/expiry controls.
|
||||
3. Phase 3: Auto-update on note change and basic analytics.
|
||||
4. Phase 4: Team branding/domains/policies.
|
||||
|
||||
## Zero-Knowledge Position
|
||||
|
||||
### Strict Zero-Knowledge Definition
|
||||
|
||||
Strict zero-knowledge means the server cannot decrypt note content at all.
|
||||
|
||||
### Why This Conflicts with MCP and Search
|
||||
|
||||
If server cannot decrypt:
|
||||
|
||||
1. MCP tool execution against cloud content cannot read/write semantic content.
|
||||
2. Full-text search cannot index plaintext content.
|
||||
3. Semantic/vector search cannot generate or query embeddings on plaintext.
|
||||
4. Server-side relation resolution and context building become severely limited.
|
||||
|
||||
This matches earlier findings: strict zero-knowledge materially handicaps MCP-driven behavior and
|
||||
search quality.
|
||||
|
||||
### Viable Alternatives (Not Strict Zero-Knowledge)
|
||||
|
||||
1. Encryption at rest/in transit with server-side decrypt in trusted runtime.
|
||||
- Preserves MCP/search quality.
|
||||
- Not zero-knowledge cryptographically.
|
||||
|
||||
2. Client-side retrieval mode.
|
||||
- Keep MCP/search local; cloud is sync/share/backup relay.
|
||||
- Best for privacy-first users.
|
||||
- Requires local agent availability for advanced retrieval.
|
||||
|
||||
3. Limited encrypted indexing.
|
||||
- Blind indexes for exact keywords only.
|
||||
- No high-quality semantic search.
|
||||
- Usually poor UX for natural-language memory recall.
|
||||
|
||||
### Recommendation
|
||||
|
||||
For Local+:
|
||||
|
||||
1. Do not promise strict zero-knowledge for cloud MCP/search paths.
|
||||
2. Offer a privacy-first local mode where advanced retrieval stays local.
|
||||
3. Clearly label tradeoffs:
|
||||
- "Local private mode" (best privacy, best local retrieval).
|
||||
- "Cloud-assisted mode" (best cross-device/MCP consistency, trusted-runtime decrypt).
|
||||
|
||||
This keeps messaging honest and avoids repeating the known incompatibility.
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
# SPEC-SCHEMA-IMPL: Schema System Implementation Plan
|
||||
|
||||
**Status:** Draft
|
||||
**Created:** 2025-02-06
|
||||
**Branch:** `feature/schema-system`
|
||||
**Depends on:** [SPEC-SCHEMA](SPEC-SCHEMA.md)
|
||||
|
||||
## Overview
|
||||
|
||||
Implementation plan for the Basic Memory Schema System. The system is entirely programmatic —
|
||||
no LLM agent runtime or API key required. The LLM already in the user's session (Claude Code,
|
||||
Claude Desktop, etc.) provides the intelligence layer by reading schema notes via existing
|
||||
MCP tools.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Entry Points │
|
||||
│ CLI (bm schema ...) │ MCP (schema_validate) │
|
||||
└──────────┬────────────┴──────────┬──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Schema Service Layer │
|
||||
│ resolve_schema · validate · infer · diff │
|
||||
└──────────┬────────────────────────┬──────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌────────────────────────┐
|
||||
│ Picoschema Parser │ │ Note/Entity Access │
|
||||
│ YAML → SchemaModel │ │ (existing repository) │
|
||||
└──────────────────────┘ └────────────────────────┘
|
||||
```
|
||||
|
||||
No new database tables. Schemas are notes with `type: schema` — they're already indexed.
|
||||
Validation reads observations and relations from existing data.
|
||||
|
||||
## Components
|
||||
|
||||
### 1. Picoschema Parser
|
||||
|
||||
**Location:** `src/basic_memory/schema/parser.py`
|
||||
|
||||
Parses Picoschema YAML into an internal representation.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class SchemaField:
|
||||
name: str
|
||||
type: str # string, integer, number, boolean, any, or EntityName
|
||||
required: bool # True unless field name ends with ?
|
||||
is_array: bool # True if (array) notation
|
||||
is_enum: bool # True if (enum) notation
|
||||
enum_values: list[str] # Populated for enums
|
||||
description: str | None # Text after comma
|
||||
is_entity_ref: bool # True if type is capitalized (entity reference)
|
||||
children: list[SchemaField] # For (object) types
|
||||
|
||||
|
||||
@dataclass
|
||||
class SchemaDefinition:
|
||||
entity: str # The entity type this schema describes
|
||||
version: int # Schema version
|
||||
fields: list[SchemaField] # Parsed fields
|
||||
validation_mode: str # "warn" | "strict" | "off"
|
||||
frontmatter_fields: list[SchemaField] # From settings.frontmatter (default: [])
|
||||
|
||||
|
||||
def parse_picoschema(yaml_dict: dict) -> list[SchemaField]:
|
||||
"""Parse a Picoschema YAML dict into a list of SchemaField objects."""
|
||||
|
||||
|
||||
def parse_schema_note(frontmatter: dict) -> SchemaDefinition:
|
||||
"""Parse a full schema note's frontmatter into a SchemaDefinition."""
|
||||
```
|
||||
|
||||
**Input/Output:**
|
||||
```yaml
|
||||
# Input (YAML dict from frontmatter)
|
||||
schema:
|
||||
name: string, full name
|
||||
role?: string, job title
|
||||
works_at?: Organization, employer
|
||||
expertise?(array): string, areas of knowledge
|
||||
```
|
||||
|
||||
```python
|
||||
# Output
|
||||
[
|
||||
SchemaField(name="name", type="string", required=True, description="full name", ...),
|
||||
SchemaField(name="role", type="string", required=False, description="job title", ...),
|
||||
SchemaField(name="works_at", type="Organization", required=False, is_entity_ref=True, ...),
|
||||
SchemaField(name="expertise", type="string", required=False, is_array=True, ...),
|
||||
]
|
||||
```
|
||||
|
||||
### 2. Schema Resolver
|
||||
|
||||
**Location:** `src/basic_memory/schema/resolver.py`
|
||||
|
||||
Finds the applicable schema for a note using the resolution order.
|
||||
|
||||
```python
|
||||
async def resolve_schema(
|
||||
note_frontmatter: dict,
|
||||
search_fn: Callable, # injected search capability
|
||||
) -> SchemaDefinition | None:
|
||||
"""Resolve schema for a note.
|
||||
|
||||
Resolution order:
|
||||
1. Inline schema (frontmatter['schema'] is a dict)
|
||||
2. Explicit reference (frontmatter['schema'] is a string)
|
||||
3. Implicit by type (frontmatter['type'] → schema note with matching entity)
|
||||
4. No schema (returns None)
|
||||
"""
|
||||
```
|
||||
|
||||
### 3. Schema Validator
|
||||
|
||||
**Location:** `src/basic_memory/schema/validator.py`
|
||||
|
||||
Validates a note's observations and relations against a resolved schema.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FieldResult:
|
||||
field: SchemaField
|
||||
status: str # "present" | "missing" | "type_mismatch"
|
||||
values: list[str] # Matched observation values or relation targets
|
||||
message: str | None # Human-readable detail
|
||||
|
||||
|
||||
@dataclass
|
||||
class ValidationResult:
|
||||
note_identifier: str
|
||||
schema_entity: str
|
||||
passed: bool # True if no errors (warnings are OK)
|
||||
field_results: list[FieldResult]
|
||||
unmatched_observations: dict[str, int] # category → count
|
||||
unmatched_relations: list[str] # relation types not in schema
|
||||
warnings: list[str]
|
||||
errors: list[str]
|
||||
|
||||
|
||||
async def validate_note(
|
||||
note: Note,
|
||||
schema: SchemaDefinition,
|
||||
frontmatter: dict | None = None,
|
||||
) -> ValidationResult:
|
||||
"""Validate a note against a schema definition.
|
||||
|
||||
Mapping rules:
|
||||
- field: string → observation [field] exists
|
||||
- field?(array): type → multiple [field] observations
|
||||
- field?: EntityType → relation 'field [[...]]' exists
|
||||
- field?(enum): [v] → observation [field] value ∈ enum values
|
||||
- settings.frontmatter field → frontmatter key presence/value
|
||||
"""
|
||||
```
|
||||
|
||||
### 4. Schema Inference Engine
|
||||
|
||||
**Location:** `src/basic_memory/schema/inference.py`
|
||||
|
||||
Analyzes notes of a given type and suggests a schema based on usage frequency.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FieldFrequency:
|
||||
name: str
|
||||
source: str # "observation" | "relation"
|
||||
count: int # notes containing this field
|
||||
total: int # total notes analyzed
|
||||
percentage: float
|
||||
sample_values: list[str] # representative values
|
||||
is_array: bool # True if typically appears multiple times per note
|
||||
target_type: str | None # For relations, the most common target entity type
|
||||
|
||||
|
||||
@dataclass
|
||||
class InferenceResult:
|
||||
entity_type: str
|
||||
notes_analyzed: int
|
||||
field_frequencies: list[FieldFrequency]
|
||||
suggested_schema: dict # Ready-to-use Picoschema YAML dict
|
||||
suggested_required: list[str]
|
||||
suggested_optional: list[str]
|
||||
excluded: list[str] # Below threshold
|
||||
|
||||
|
||||
async def infer_schema(
|
||||
entity_type: str,
|
||||
notes: list[Note],
|
||||
required_threshold: float = 0.95, # 95%+ = required
|
||||
optional_threshold: float = 0.25, # 25%+ = optional
|
||||
) -> InferenceResult:
|
||||
"""Analyze notes and suggest a Picoschema definition."""
|
||||
```
|
||||
|
||||
### 5. Schema Diff
|
||||
|
||||
**Location:** `src/basic_memory/schema/diff.py`
|
||||
|
||||
Compares current note usage against an existing schema definition.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class SchemaDrift:
|
||||
new_fields: list[FieldFrequency] # Fields not in schema but common in notes
|
||||
dropped_fields: list[FieldFrequency] # Fields in schema but rare in notes
|
||||
cardinality_changes: list[str] # one → many or many → one
|
||||
type_mismatches: list[str] # observation values don't match declared type
|
||||
|
||||
|
||||
async def diff_schema(
|
||||
schema: SchemaDefinition,
|
||||
notes: list[Note],
|
||||
) -> SchemaDrift:
|
||||
"""Compare a schema against actual note usage to detect drift."""
|
||||
```
|
||||
|
||||
## Entry Points
|
||||
|
||||
### CLI Commands
|
||||
|
||||
**Location:** `src/basic_memory/cli/schema.py`
|
||||
|
||||
```python
|
||||
import typer
|
||||
|
||||
schema_app = typer.Typer(name="schema", help="Schema management commands")
|
||||
|
||||
@schema_app.command()
|
||||
async def validate(
|
||||
target: str = typer.Argument(None, help="Note path or entity type"),
|
||||
strict: bool = typer.Option(False, help="Override to strict mode"),
|
||||
):
|
||||
"""Validate notes against their schemas."""
|
||||
|
||||
@schema_app.command()
|
||||
async def infer(
|
||||
entity_type: str = typer.Argument(..., help="Entity type to analyze"),
|
||||
threshold: float = typer.Option(0.25, help="Minimum frequency for optional fields"),
|
||||
save: bool = typer.Option(False, help="Save to schema/ directory"),
|
||||
):
|
||||
"""Infer schema from existing notes of a type."""
|
||||
|
||||
@schema_app.command()
|
||||
async def diff(
|
||||
entity_type: str = typer.Argument(..., help="Entity type to diff"),
|
||||
):
|
||||
"""Show drift between schema and actual usage."""
|
||||
```
|
||||
|
||||
Registered as subcommand: `bm schema validate`, `bm schema infer`, `bm schema diff`.
|
||||
|
||||
### MCP Tools
|
||||
|
||||
**Location:** `src/basic_memory/mcp/tools/schema.py`
|
||||
|
||||
```python
|
||||
@mcp_tool
|
||||
async def schema_validate(
|
||||
entity_type: str | None = None,
|
||||
identifier: str | None = None,
|
||||
project: str | None = None,
|
||||
) -> str:
|
||||
"""Validate notes against their resolved schema."""
|
||||
|
||||
@mcp_tool
|
||||
async def schema_infer(
|
||||
entity_type: str,
|
||||
threshold: float = 0.25,
|
||||
project: str | None = None,
|
||||
) -> str:
|
||||
"""Analyze existing notes and suggest a schema definition."""
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
|
||||
**Location:** `src/basic_memory/api/schema_router.py`
|
||||
|
||||
```python
|
||||
router = APIRouter(prefix="/schema", tags=["schema"])
|
||||
|
||||
@router.post("/validate")
|
||||
async def validate_schema(...) -> ValidationReport: ...
|
||||
|
||||
@router.post("/infer")
|
||||
async def infer_schema(...) -> InferenceResult: ...
|
||||
|
||||
@router.get("/diff/{entity_type}")
|
||||
async def diff_schema(...) -> SchemaDrift: ...
|
||||
```
|
||||
|
||||
MCP tools call these endpoints via the typed client pattern (consistent with existing
|
||||
architecture).
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Parser + Resolver
|
||||
|
||||
Build the foundation — can parse Picoschema and find schemas for notes.
|
||||
|
||||
**Deliverables:**
|
||||
- `schema/parser.py` — Picoschema YAML → `SchemaDefinition`
|
||||
- `schema/resolver.py` — Resolution order (inline → explicit ref → implicit by type → none)
|
||||
- Unit tests for all Picoschema syntax variations
|
||||
- Unit tests for resolution order
|
||||
|
||||
**No external dependencies.** Pure Python parsing of YAML dicts. Can develop and test
|
||||
in isolation.
|
||||
|
||||
### Phase 2: Validator
|
||||
|
||||
Connect schemas to notes and produce validation results.
|
||||
|
||||
**Deliverables:**
|
||||
- `schema/validator.py` — Validate note observations/relations against schema fields
|
||||
- API endpoint: `POST /schema/validate`
|
||||
- MCP tool: `schema_validate`
|
||||
- CLI command: `bm schema validate`
|
||||
- Integration tests with real notes and schemas
|
||||
|
||||
**Depends on:** Phase 1 (parser + resolver)
|
||||
|
||||
### Phase 3: Inference
|
||||
|
||||
Analyze existing notes to suggest schemas.
|
||||
|
||||
**Deliverables:**
|
||||
- `schema/inference.py` — Frequency analysis across notes of a type
|
||||
- API endpoint: `POST /schema/infer`
|
||||
- MCP tool: `schema_infer`
|
||||
- CLI command: `bm schema infer`
|
||||
- Option to save inferred schema as a note via `write_note`
|
||||
|
||||
**Depends on:** Phase 1 (parser for output format)
|
||||
|
||||
### Phase 4: Diff
|
||||
|
||||
Compare schemas against current usage.
|
||||
|
||||
**Deliverables:**
|
||||
- `schema/diff.py` — Drift detection between schema and actual notes
|
||||
- API endpoint: `GET /schema/diff/{entity_type}`
|
||||
- CLI command: `bm schema diff`
|
||||
|
||||
**Depends on:** Phase 1 (parser), Phase 3 (inference, for frequency analysis)
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- **Unit tests** (`tests/schema/`): Parser edge cases, resolution logic, validation mapping,
|
||||
inference thresholds
|
||||
- **Integration tests** (`test-int/schema/`): End-to-end with real markdown files, schema notes
|
||||
on disk, CLI invocation
|
||||
- Coverage target: 100% (consistent with project standard)
|
||||
|
||||
## What This Does NOT Include
|
||||
|
||||
- No new database tables or migrations
|
||||
- No new markdown syntax (schemas validate existing observations/relations)
|
||||
- No LLM agent runtime or API key management
|
||||
- No hook integration (deferred)
|
||||
- No schema composition/inheritance (deferred)
|
||||
- No OWL/RDF export (deferred)
|
||||
- No built-in templates (deferred)
|
||||
@@ -0,0 +1,492 @@
|
||||
# SPEC-SCHEMA: Basic Memory Schema System
|
||||
|
||||
**Status:** Draft
|
||||
**Created:** 2025-02-06
|
||||
**Branch:** `feature/schema-system`
|
||||
|
||||
## Summary
|
||||
|
||||
A schema system for Basic Memory that uses [Picoschema](https://genkit.dev/docs/dotprompt/)
|
||||
syntax in YAML frontmatter. Schemas validate notes against their existing observation/relation
|
||||
structure — no new data model, no migration, just a declarative lens over what's already there.
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Schemas are just notes** — A schema is a note with `type: schema`, lives anywhere
|
||||
2. **Use prior art** — Picoschema syntax in YAML frontmatter, no custom notation
|
||||
3. **Validation maps to existing format** — Observations and relations, not a parallel data model
|
||||
4. **Validation is soft** — Warnings by default, not blocking errors
|
||||
5. **Inference over prescription** — Schemas describe reality, emerge from usage
|
||||
6. **No built-in agent** — Programmatic core; the LLM already in the session provides intelligence
|
||||
|
||||
## Picoschema Syntax
|
||||
|
||||
Picoschema is a compact schema notation from Google's Dotprompt that fits naturally in YAML
|
||||
frontmatter.
|
||||
|
||||
### Supported Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `string` | Text value |
|
||||
| `integer` | Whole number |
|
||||
| `number` | Decimal number |
|
||||
| `boolean` | True/false |
|
||||
| `any` | Any scalar type |
|
||||
| `EntityName` | Reference to another entity (capitalized = entity reference) |
|
||||
|
||||
### Syntax Rules
|
||||
|
||||
```yaml
|
||||
schema:
|
||||
name: string, full name # required field with description
|
||||
email?: string, contact email # ? = optional
|
||||
role?: string, job title
|
||||
works_at?: Organization, employer # capitalized type = entity reference
|
||||
tags?(array): string, categories # array of type
|
||||
status?(enum): [active, inactive] # enum with allowed values
|
||||
metadata?(object): # nested object
|
||||
updated_at?: string
|
||||
source?: string
|
||||
```
|
||||
|
||||
- `field: type` — required field
|
||||
- `field?: type` — optional field
|
||||
- `field(array): type` — array of values
|
||||
- `field?(enum): [values]` — enumeration
|
||||
- `field?(object):` — nested object with sub-fields
|
||||
- `, description` — description after comma
|
||||
- `EntityName` as type (capitalized) — reference to another entity
|
||||
|
||||
## Schema-to-Note Mapping
|
||||
|
||||
Schemas validate against the existing Basic Memory note format. No new syntax for note
|
||||
authors to learn.
|
||||
|
||||
### Mapping Rules
|
||||
|
||||
| Schema Declaration | Grounded In | Example Match |
|
||||
|--------------------|-------------|---------------|
|
||||
| `field: string` | Observation `[field] value` | `- [name] Paul Graham` |
|
||||
| `field?(array): string` | Multiple `[field]` observations | `- [expertise] Lisp` (×N) |
|
||||
| `field?: EntityType` | Relation `field [[Target]]` | `- works_at [[Y Combinator]]` |
|
||||
| `field?(array): EntityType` | Multiple `field` relations | `- authored [[Book]]` (×N) |
|
||||
| `tags` | Frontmatter `tags` array | `tags: [startups, essays]` |
|
||||
| `field?(enum): [values]` | Observation `[field] value` where value ∈ set | `- [status] active` |
|
||||
| `settings.frontmatter` field | Frontmatter key presence/value | `tags: [python, ai]` |
|
||||
|
||||
### Key Insight
|
||||
|
||||
Schemas don't introduce a new way to store data. They describe the patterns already present
|
||||
in observations and relations. A note doesn't have to change how it's written — the schema
|
||||
just says "a good Person note has a `[name]` observation and a `works_at` relation."
|
||||
|
||||
## Schema Definition
|
||||
|
||||
### As a Dedicated Schema Note
|
||||
|
||||
```yaml
|
||||
# schema/Person.md
|
||||
---
|
||||
title: Person
|
||||
type: schema
|
||||
entity: Person
|
||||
version: 1
|
||||
schema:
|
||||
name: string, full name
|
||||
email?: string, contact email
|
||||
role?: string, job title
|
||||
works_at?: Organization, employer
|
||||
expertise?(array): string, areas of knowledge
|
||||
settings:
|
||||
validation: warn # warn | strict | off
|
||||
frontmatter:
|
||||
tags?(array): string, note categories
|
||||
status?(enum): [draft, review, published]
|
||||
---
|
||||
|
||||
# Person
|
||||
|
||||
A human individual in the knowledge graph.
|
||||
|
||||
Any documentation about this entity type goes here as prose.
|
||||
```
|
||||
|
||||
Schema notes are regular Basic Memory notes. They show up in search, can have their own
|
||||
observations and relations, and can be organized in any folder (though `schema/` is
|
||||
the suggested convention).
|
||||
|
||||
### Inline Schema in a Note
|
||||
|
||||
Notes can carry their own schema directly:
|
||||
|
||||
```yaml
|
||||
# meetings/2024-01-15-standup.md
|
||||
---
|
||||
title: Team Standup 2024-01-15
|
||||
type: meeting
|
||||
schema:
|
||||
attendees(array): string, who was there
|
||||
decisions(array): string, what was decided
|
||||
action_items(array): string, follow-ups
|
||||
blockers?(array): string, anything stuck
|
||||
---
|
||||
|
||||
# Team Standup 2024-01-15
|
||||
|
||||
## Observations
|
||||
- [attendees] Paul
|
||||
- [attendees] Sarah
|
||||
- [decisions] Ship v2 by Friday
|
||||
- [action_items] Paul to review PR #42
|
||||
- [blockers] Waiting on API credentials
|
||||
```
|
||||
|
||||
Good for one-off structured notes or prototyping a schema before extracting it.
|
||||
|
||||
### Explicit Schema Reference
|
||||
|
||||
A note can reference a schema by entity name or permalink:
|
||||
|
||||
```yaml
|
||||
# projects/basic-memory.md
|
||||
---
|
||||
title: Basic Memory
|
||||
schema: SoftwareProject # by entity name
|
||||
---
|
||||
|
||||
# research/llm-memory-patterns.md
|
||||
---
|
||||
title: LLM Memory Patterns
|
||||
schema: schema/research-project # by permalink
|
||||
---
|
||||
```
|
||||
|
||||
Use cases:
|
||||
- Note's `type` differs from the schema it should validate against
|
||||
- Multiple schema variants exist for the same domain
|
||||
- Applying structure to existing notes without changing their type
|
||||
|
||||
## Schema Resolution
|
||||
|
||||
When validating a note, schemas resolve in priority order:
|
||||
|
||||
```
|
||||
1. Inline schema → schema: { ... } (dict in frontmatter)
|
||||
2. Explicit ref → schema: Person (string in frontmatter)
|
||||
3. Implicit by type → type: Person (lookup schema note with entity: Person)
|
||||
4. No schema → no validation (perfectly fine)
|
||||
```
|
||||
|
||||
```python
|
||||
async def resolve_schema(note: Note) -> Schema | None:
|
||||
schema_value = note.frontmatter.get('schema')
|
||||
|
||||
# 1. Inline schema (dict)
|
||||
if isinstance(schema_value, dict):
|
||||
return parse_picoschema(schema_value)
|
||||
|
||||
# 2. Explicit reference (string)
|
||||
if isinstance(schema_value, str):
|
||||
schema_note = await find_schema_note(schema_value)
|
||||
if schema_note:
|
||||
return parse_picoschema(schema_note.frontmatter['schema'])
|
||||
|
||||
# 3. Implicit by type
|
||||
note_type = note.frontmatter.get('type')
|
||||
if note_type:
|
||||
results = await search_notes(f"type:schema entity:{note_type}")
|
||||
if results:
|
||||
return parse_picoschema(results[0].frontmatter['schema'])
|
||||
|
||||
# 4. No schema
|
||||
return None
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
### Modes
|
||||
|
||||
Configured in the schema's `settings.validation`:
|
||||
|
||||
| Mode | Behavior |
|
||||
|------|----------|
|
||||
| `off` | No validation |
|
||||
| `warn` | Warnings in output, doesn't block (default) |
|
||||
| `strict` | Errors that block sync, for CI/CD enforcement |
|
||||
|
||||
### Validation Output
|
||||
|
||||
For a note missing required fields:
|
||||
|
||||
```
|
||||
$ bm schema validate people/ada-lovelace.md
|
||||
|
||||
⚠ Person schema validation:
|
||||
- Missing required field: name (expected [name] observation)
|
||||
- Missing optional field: role
|
||||
- Missing optional field: works_at (no relation found)
|
||||
|
||||
ℹ Unmatched observations: [fact] ×2, [born] ×1
|
||||
ℹ Unmatched relations: collaborated_with
|
||||
```
|
||||
|
||||
"Unmatched" items are informational — observations and relations the schema doesn't cover.
|
||||
They're valid. Schemas are a subset, not a straitjacket.
|
||||
|
||||
### Frontmatter Validation
|
||||
|
||||
Schema notes can declare validation rules for frontmatter keys under `settings.frontmatter`
|
||||
using the same Picoschema syntax as the `schema` block:
|
||||
|
||||
```yaml
|
||||
settings:
|
||||
validation: warn
|
||||
frontmatter:
|
||||
tags?(array): string
|
||||
status?(enum): [draft, review, published]
|
||||
```
|
||||
|
||||
- Frontmatter rules use the same Picoschema key syntax (`?` for optional, `(enum)`, `(array)`)
|
||||
- Only available on schema notes (inline schemas skip frontmatter validation)
|
||||
- Checks key presence (required vs optional) and enum value membership
|
||||
- Unmatched frontmatter keys not in the schema are silently ignored
|
||||
- Missing required frontmatter keys produce a warning (or error in strict mode)
|
||||
|
||||
Example output for a missing required frontmatter key:
|
||||
|
||||
```
|
||||
⚠ Person schema validation:
|
||||
- Missing required frontmatter key: status
|
||||
```
|
||||
|
||||
### Batch Validation
|
||||
|
||||
```
|
||||
$ bm schema validate Person
|
||||
|
||||
Validating 30 notes against Person schema...
|
||||
|
||||
✓ people/paul-graham.md — all fields present
|
||||
✓ people/rich-hickey.md — all fields present
|
||||
⚠ people/ada-lovelace.md — missing: name
|
||||
⚠ people/alan-kay.md — missing: name, role
|
||||
✓ people/linus-torvalds.md — all fields present
|
||||
...
|
||||
|
||||
Summary: 22/30 valid, 8 warnings, 0 errors
|
||||
```
|
||||
|
||||
## Emerging Schemas
|
||||
|
||||
### The Problem with Traditional Schemas
|
||||
|
||||
Most schema systems require: define schema → create conforming content → fight the schema
|
||||
when reality doesn't match. This is backwards. Knowledge grows organically.
|
||||
|
||||
### The Basic Memory Approach
|
||||
|
||||
```
|
||||
Write notes freely → Patterns emerge → Crystallize into schema → Validate future notes
|
||||
```
|
||||
|
||||
### Schema Inference
|
||||
|
||||
Generate schemas from existing notes by analyzing observation and relation frequency:
|
||||
|
||||
```
|
||||
$ bm schema infer Person
|
||||
|
||||
Analyzing 30 notes with type: Person...
|
||||
|
||||
Observations found:
|
||||
[name] 30/30 100% → name: string
|
||||
[role] 27/30 90% → role?: string
|
||||
[fact] 25/30 83% (generic — no single field)
|
||||
[expertise] 18/30 60% → expertise?(array): string
|
||||
[email] 8/30 27% → email?: string
|
||||
[born] 6/30 20% (below threshold)
|
||||
|
||||
Relations found:
|
||||
works_at 22/30 73% → works_at?: Organization
|
||||
authored 11/30 37% → authored?(array): string
|
||||
|
||||
Suggested schema:
|
||||
name: string, full name
|
||||
role?: string, job title
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
works_at?: Organization, employer
|
||||
|
||||
Save to schema/Person.md? [y/n]
|
||||
```
|
||||
|
||||
Frequency thresholds:
|
||||
- 100% present → required field
|
||||
- 25%+ present → optional field
|
||||
- Below 25% → excluded from suggestion (but noted)
|
||||
|
||||
### Schema Drift Detection
|
||||
|
||||
Track how usage patterns shift over time:
|
||||
|
||||
```
|
||||
$ bm schema diff Person
|
||||
|
||||
Schema drift detected:
|
||||
|
||||
+ expertise: now in 81% of notes (was 12%)
|
||||
- department: dropped to 3% of notes
|
||||
~ works_at: cardinality changed (one → many)
|
||||
|
||||
Update schema? [y/n/review]
|
||||
```
|
||||
|
||||
## LLM Integration (AI Guidance)
|
||||
|
||||
No agent runtime or API key required. The LLM already in the session uses schemas as
|
||||
context for note creation.
|
||||
|
||||
### Flow
|
||||
|
||||
1. User asks LLM to "write a note about Rich Hickey"
|
||||
2. LLM determines `type: Person` is appropriate
|
||||
3. LLM calls `search_notes("type:schema entity:Person")` → finds schema
|
||||
4. LLM reads schema fields: required `name`, optional `role`, `works_at`, `expertise`
|
||||
5. LLM calls `write_note` with observations and relations that satisfy the schema
|
||||
|
||||
The schema acts as a creation template. The LLM knows what a "complete" note looks like
|
||||
without any custom agent infrastructure.
|
||||
|
||||
### MCP Tools
|
||||
|
||||
```python
|
||||
@mcp_tool
|
||||
async def schema_validate(
|
||||
entity_type: str | None = None,
|
||||
identifier: str | None = None,
|
||||
project: str | None = None,
|
||||
) -> ValidationReport:
|
||||
"""Validate notes against their resolved schema.
|
||||
|
||||
Validates a specific note (by identifier) or all notes of a given type.
|
||||
Returns warnings/errors based on the schema's validation mode.
|
||||
"""
|
||||
|
||||
@mcp_tool
|
||||
async def schema_infer(
|
||||
entity_type: str,
|
||||
threshold: float = 0.25,
|
||||
project: str | None = None,
|
||||
) -> SuggestedSchema:
|
||||
"""Analyze existing notes and suggest a schema definition.
|
||||
|
||||
Examines observation categories and relation types across all notes
|
||||
of the given type. Returns frequency analysis and suggested Picoschema.
|
||||
"""
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
```bash
|
||||
# Validate a specific note
|
||||
bm schema validate people/ada-lovelace.md
|
||||
|
||||
# Validate all notes of a type
|
||||
bm schema validate Person
|
||||
|
||||
# Validate everything with a schema
|
||||
bm schema validate
|
||||
|
||||
# Infer schema from existing notes
|
||||
bm schema infer Person
|
||||
|
||||
# Show schema drift from current definition
|
||||
bm schema diff Person
|
||||
|
||||
# List all schema notes
|
||||
bm search "type:schema"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Complete Person Workflow
|
||||
|
||||
**Schema:**
|
||||
```yaml
|
||||
# schema/Person.md
|
||||
---
|
||||
title: Person
|
||||
type: schema
|
||||
entity: Person
|
||||
version: 1
|
||||
schema:
|
||||
name: string, full name
|
||||
role?: string, job title or position
|
||||
works_at?: Organization, employer
|
||||
expertise?(array): string, areas of knowledge
|
||||
email?: string, contact email
|
||||
settings:
|
||||
validation: warn
|
||||
---
|
||||
|
||||
# Person
|
||||
|
||||
A human individual in the knowledge graph.
|
||||
```
|
||||
|
||||
**Valid note:**
|
||||
```yaml
|
||||
# people/paul-graham.md
|
||||
---
|
||||
title: Paul Graham
|
||||
type: Person
|
||||
tags: [startups, essays, lisp]
|
||||
---
|
||||
|
||||
# Paul Graham
|
||||
|
||||
## Observations
|
||||
- [name] Paul Graham
|
||||
- [role] Essayist and investor
|
||||
- [expertise] Startups
|
||||
- [expertise] Lisp
|
||||
- [expertise] Essay writing
|
||||
- [fact] Created Viaweb, the first web app
|
||||
|
||||
## Relations
|
||||
- works_at [[Y Combinator]]
|
||||
- authored [[Hackers and Painters]]
|
||||
```
|
||||
|
||||
**Note with warnings:**
|
||||
```yaml
|
||||
# people/ada-lovelace.md
|
||||
---
|
||||
title: Ada Lovelace
|
||||
type: Person
|
||||
---
|
||||
|
||||
# Ada Lovelace
|
||||
|
||||
## Observations
|
||||
- [fact] Wrote the first computer program
|
||||
- [born] 1815
|
||||
|
||||
## Relations
|
||||
- collaborated_with [[Charles Babbage]]
|
||||
```
|
||||
|
||||
Validation: warns about missing required `[name]` observation. Everything else is optional
|
||||
or unmatched (which is fine).
|
||||
|
||||
## Future Considerations (Deferred)
|
||||
|
||||
These are interesting but out of scope for the initial implementation:
|
||||
|
||||
- **Multiple schema inheritance** — `schema: [Person, Author]`
|
||||
- **Hook integration** — Pre-write validation via the hooks system
|
||||
- **OWL/RDF export** — `bm schema export --format owl`
|
||||
- **SPARQL queries** — Schema-aware graph queries
|
||||
- **Built-in templates** — `bm schema use gtd`, `bm schema use zettelkasten`
|
||||
- **Schema versioning/migration** — Tracking breaking changes across versions
|
||||
@@ -0,0 +1,28 @@
|
||||
## Coverage policy (practical 100%)
|
||||
|
||||
Basic Memory’s test suite intentionally mixes:
|
||||
- unit tests (fast, deterministic)
|
||||
- integration tests (real filesystem + real DB via `test-int/`)
|
||||
|
||||
To keep the default CI signal **stable and meaningful**, the default `pytest` coverage report targets **core library logic** and **excludes** a small set of modules that are either:
|
||||
- highly environment-dependent (OS/DB tuning)
|
||||
- inherently interactive (CLI)
|
||||
- background-task orchestration (watchers/sync runners)
|
||||
|
||||
### What's excluded (and why)
|
||||
|
||||
Coverage excludes are configured in `pyproject.toml` under `[tool.coverage.report].omit`.
|
||||
|
||||
Current exclusions include:
|
||||
- `src/basic_memory/cli/**`: interactive wrappers; behavior is validated via higher-level tests and smoke tests.
|
||||
- `src/basic_memory/db.py`: platform/backend tuning paths (SQLite/Postgres/Windows), covered by integration tests and targeted runs.
|
||||
- `src/basic_memory/services/initialization.py`: startup orchestration/background tasks; covered indirectly by app/MCP entrypoints.
|
||||
- `src/basic_memory/sync/sync_service.py`: heavy filesystem↔DB integration; validated in integration suite (not enforced in unit coverage).
|
||||
|
||||
### Recommended additional runs
|
||||
|
||||
If you want extra confidence locally/CI:
|
||||
- **Postgres backend**: run tests with `BASIC_MEMORY_TEST_POSTGRES=1`.
|
||||
- **Strict backend-complete coverage**: run coverage on SQLite + Postgres and combine the results (recommended).
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"promptDelete": false
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,3 +0,0 @@
|
||||
[
|
||||
"optimize-canvas-connections"
|
||||
]
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"}
|
||||
]
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
title: Tasting Notes
|
||||
type: note
|
||||
permalink: coffee/tasting-notes
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#tasting'
|
||||
- '#flavor'
|
||||
- '#cupping'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Tasting Notes
|
||||
|
||||
How to taste and evaluate coffee, identify flavor characteristics, and develop a personal coffee palate.
|
||||
|
||||
## Overview
|
||||
|
||||
Coffee tasting, or "cupping" in professional contexts, is the practice of observing the tastes and aromas of brewed coffee. Developing a coffee palate helps identify preferences, communicate about coffee experiences, and better understand how brewing variables affect the cup.
|
||||
|
||||
## Observations
|
||||
|
||||
- [principle] Flavor perception includes taste, aroma, mouthfeel, and retronasal perception #sensory
|
||||
- [principle] Our taste buds can only perceive sweet, sour, salty, bitter, and umami #taste
|
||||
- [principle] Most of what we call "flavor" is actually aroma detected retronasally #aroma
|
||||
- [technique] Professional coffee tasting (cupping) uses a standardized protocol for consistency #cupping
|
||||
- [technique] Slurping coffee aerates it and spreads it across all taste receptors #technique
|
||||
- [technique] Allowing coffee to cool reveals different flavor notes at different temperatures #temperature
|
||||
|
||||
## Coffee Flavor Wheel
|
||||
|
||||
- [tool] The SCA Coffee Flavor Wheel provides a standardized vocabulary for describing coffee #flavor_wheel
|
||||
- [category] Primary categories include: Fruity, Floral, Sweet, Nutty/Cocoa, Spice, Roasted, Other #categories
|
||||
- [subcategory] Fruity breaks down into: Berry, Dried Fruit, Citrus Fruit, Stone Fruit, Tropical Fruit, etc. #fruit_notes
|
||||
- [subcategory] Floral includes: Floral, Black Tea, Chamomile, Rose, Jasmine, etc. #floral_notes
|
||||
- [subcategory] Sweet includes: Brown Sugar, Molasses, Honey, Maple Syrup, Vanilla, etc. #sweet_notes
|
||||
- [subcategory] Nutty/Cocoa includes: Nut, Cocoa, Dark Chocolate, Chocolate, etc. #nutty_notes
|
||||
- [subcategory] Spice includes: Brown Spice, Pepper, Anise, Nutmeg, Cinnamon, etc. #spice_notes
|
||||
|
||||
## Basic Tasting Components
|
||||
|
||||
- [component] Acidity: The bright, tangy quality (not sourness from under-extraction) #acidity
|
||||
- [component] Sweetness: The pleasant, sugary quality balancing other elements #sweetness
|
||||
- [component] Body: The physical mouthfeel and weight of the coffee #body
|
||||
- [component] Finish/Aftertaste: The flavor that lingers after swallowing #finish
|
||||
- [component] Balance: How well all elements work together #balance
|
||||
- [component] Complexity: The range and layers of distinct flavors #complexity
|
||||
- [component] Cleanliness: Absence of defects or off-flavors #cleanliness
|
||||
|
||||
## Common Flavor Notes by Origin
|
||||
|
||||
- [ethiopia] Blueberry, jasmine, bergamot, lemon, tea-like #flavor_notes
|
||||
- [kenya] Blackcurrant, grapefruit, tomato-like acidity, winey #flavor_notes
|
||||
- [colombia] Caramel, red apple, nuts, chocolate, balanced acidity #flavor_notes
|
||||
- [guatemala] Chocolate, spice, apple, medium acidity #flavor_notes
|
||||
- [brazil] Nuts, chocolate, low-to-medium acidity, full body #flavor_notes
|
||||
- [indonesia] Earthy, herbal, spice, cedar, full body, low acidity #flavor_notes
|
||||
- [costa_rica] Clean, bright, citrus, balanced, light chocolate #flavor_notes
|
||||
|
||||
## Developing Your Palate
|
||||
|
||||
- [technique] Taste coffees side-by-side to identify differences #comparison
|
||||
- [technique] Try describing flavors before looking at roaster's notes #blind_tasting
|
||||
- [technique] Keep a coffee journal with detailed notes about each coffee #journaling
|
||||
- [technique] Explore different processing methods of the same origin #processing
|
||||
- [technique] Try the same coffee brewed with different methods #brewing_comparison
|
||||
- [technique] Use reference flavors (actual fruits, chocolates, etc.) to calibrate your palate #calibration
|
||||
|
||||
## Personal Coffee Experiences
|
||||
|
||||
- [experience] Ethiopian Yirgacheffe prepared as pour over: intense blueberry, jasmine aromatics, tea-like body
|
||||
- [experience] Sumatra Mandheling in French press: earthy, cedar, herbal, tobacco, full body
|
||||
- [experience] Panama Gesha as pour over: intense floral notes, jasmine, bergamot, delicate body
|
||||
- [experience] Brazil Cerrado as espresso: nutty, chocolate, caramel, low acidity, great crema
|
||||
- [experience] Kenya AA as pour over: bright blackcurrant, tomato-like acidity, winey finish
|
||||
|
||||
## Relations
|
||||
|
||||
- determined_by [[Flavor Extraction]]
|
||||
- influenced_by [[Coffee Bean Origins]]
|
||||
- varies_with [[Coffee Brewing Methods]]
|
||||
- enhanced_by [[Proper Grinding Technique]]
|
||||
- documented_in [[Coffee Journal]]
|
||||
- part_of [[Coffee Knowledge Base]]
|
||||
@@ -1,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.
|
||||
@@ -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 |
@@ -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()
|
||||
@@ -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
|
||||
@@ -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,
|
||||
)
|
||||
@@ -0,0 +1,367 @@
|
||||
# Basic Memory - Modern Command Runner
|
||||
|
||||
# Install dependencies
|
||||
install:
|
||||
uv sync
|
||||
@echo ""
|
||||
@echo "💡 Remember to activate the virtual environment by running: source .venv/bin/activate"
|
||||
|
||||
# ==============================================================================
|
||||
# DATABASE BACKEND TESTING
|
||||
# ==============================================================================
|
||||
# Basic Memory supports dual database backends (SQLite and Postgres).
|
||||
# By default, tests run against SQLite (fast, no dependencies).
|
||||
# Set BASIC_MEMORY_TEST_POSTGRES=1 to run against Postgres (uses testcontainers).
|
||||
#
|
||||
# Quick Start:
|
||||
# just test # Run all tests against SQLite (default)
|
||||
# just test-sqlite # Run all tests against SQLite
|
||||
# just test-postgres # Run all tests against Postgres (testcontainers)
|
||||
# just test-unit-sqlite # Run unit tests against SQLite
|
||||
# just test-unit-postgres # Run unit tests against Postgres
|
||||
# just test-int-sqlite # Run integration tests against SQLite
|
||||
# just test-int-postgres # Run integration tests against Postgres
|
||||
#
|
||||
# CI runs both in parallel for faster feedback.
|
||||
# ==============================================================================
|
||||
|
||||
# Run all tests against SQLite and Postgres
|
||||
test: test-sqlite test-postgres
|
||||
|
||||
# Run all tests against SQLite
|
||||
test-sqlite: test-unit-sqlite test-int-sqlite
|
||||
|
||||
# Run all tests against Postgres (uses testcontainers)
|
||||
test-postgres: test-unit-postgres test-int-postgres
|
||||
|
||||
# Run unit tests against SQLite
|
||||
test-unit-sqlite:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov tests
|
||||
|
||||
# Run unit tests against Postgres
|
||||
test-unit-postgres:
|
||||
BASIC_MEMORY_ENV=test BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov tests
|
||||
|
||||
# Run integration tests against SQLite (excludes semantic benchmarks — use just test-semantic)
|
||||
test-int-sqlite:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov -m "not semantic" test-int
|
||||
|
||||
# Run integration tests against Postgres
|
||||
# Note: Uses timeout due to FastMCP Client + asyncpg cleanup hang (tests pass, process hangs on exit)
|
||||
# See: https://github.com/jlowin/fastmcp/issues/1311
|
||||
test-int-postgres:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
# Use gtimeout (macOS/Homebrew) or timeout (Linux)
|
||||
TIMEOUT_CMD=$(command -v gtimeout || command -v timeout || echo "")
|
||||
if [[ -n "$TIMEOUT_CMD" ]]; then
|
||||
$TIMEOUT_CMD --signal=KILL 600 bash -c 'BASIC_MEMORY_ENV=test BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov -m "not semantic" test-int' || test $? -eq 137
|
||||
else
|
||||
echo "⚠️ No timeout command found, running without timeout..."
|
||||
BASIC_MEMORY_ENV=test BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov -m "not semantic" test-int
|
||||
fi
|
||||
|
||||
# Run tests impacted by recent changes (requires pytest-testmon)
|
||||
testmon *args:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov --testmon --testmon-forceselect {{args}}
|
||||
|
||||
# Run MCP smoke test (fast end-to-end loop)
|
||||
test-smoke:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov -m smoke test-int/mcp/test_smoke_integration.py
|
||||
|
||||
# Fast local loop: lint, format, typecheck, impacted tests
|
||||
fast-check:
|
||||
just fix
|
||||
just format
|
||||
just typecheck
|
||||
just testmon
|
||||
just test-smoke
|
||||
|
||||
# Reset Postgres test database (drops and recreates schema)
|
||||
# Useful when Alembic migration state gets out of sync during development
|
||||
# Uses credentials from docker-compose-postgres.yml
|
||||
postgres-reset:
|
||||
docker exec basic-memory-postgres psql -U ${POSTGRES_USER:-basic_memory_user} -d ${POSTGRES_TEST_DB:-basic_memory_test} -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
|
||||
@echo "✅ Postgres test database reset"
|
||||
|
||||
# Run Alembic migrations manually against Postgres test database
|
||||
# Useful for debugging migration issues
|
||||
# Uses credentials from docker-compose-postgres.yml (can override with env vars)
|
||||
postgres-migrate:
|
||||
@cd src/basic_memory/alembic && \
|
||||
BASIC_MEMORY_DATABASE_BACKEND=postgres \
|
||||
BASIC_MEMORY_DATABASE_URL=${POSTGRES_TEST_URL:-postgresql+asyncpg://basic_memory_user:dev_password@localhost:5433/basic_memory_test} \
|
||||
uv run alembic upgrade head
|
||||
@echo "✅ Migrations applied to Postgres test database"
|
||||
|
||||
# Run Windows-specific tests only (only works on Windows platform)
|
||||
# These tests verify Windows-specific database optimizations (locking mode, NullPool)
|
||||
# Will be skipped automatically on non-Windows platforms
|
||||
test-windows:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov -m windows tests test-int
|
||||
|
||||
# Run benchmark tests only (performance testing)
|
||||
# These are slow tests that measure sync performance with various file counts
|
||||
# Excluded from default test runs to keep CI fast
|
||||
test-benchmark:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov -m benchmark tests test-int
|
||||
|
||||
# Run semantic search quality benchmarks (all combos)
|
||||
test-semantic:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov -m semantic test-int/semantic/
|
||||
|
||||
# Run semantic benchmarks with JSON artifact output, then show report
|
||||
test-semantic-report:
|
||||
BASIC_MEMORY_ENV=test BASIC_MEMORY_BENCHMARK_OUTPUT=.benchmarks/semantic-quality.jsonl uv run pytest -p pytest_mock -v -s --no-cov -m semantic test-int/semantic/
|
||||
uv run python test-int/semantic/report.py .benchmarks/semantic-quality.jsonl
|
||||
|
||||
# Run semantic benchmarks (Postgres combos only)
|
||||
test-semantic-postgres:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov -m semantic -k postgres test-int/semantic/
|
||||
|
||||
# View semantic benchmark results (rich formatted table)
|
||||
# Usage: just semantic-report [--filter-combo sqlite] [--filter-suite paraphrase] [--sort-by avg_latency_ms]
|
||||
semantic-report *args:
|
||||
uv run python test-int/semantic/report.py .benchmarks/semantic-quality.jsonl {{args}}
|
||||
|
||||
# Compare two search benchmark JSONL outputs
|
||||
# Usage:
|
||||
# just benchmark-compare .benchmarks/search-baseline.jsonl .benchmarks/search-candidate.jsonl
|
||||
# just benchmark-compare .benchmarks/search-baseline.jsonl .benchmarks/search-candidate.jsonl --format markdown --show-missing
|
||||
benchmark-compare baseline candidate *args:
|
||||
uv run python test-int/compare_search_benchmarks.py "{{baseline}}" "{{candidate}}" --format table {{args}}
|
||||
|
||||
# Run all tests including Windows, Postgres, and Benchmarks (for CI/comprehensive testing)
|
||||
# Use this before releasing to ensure everything works across all backends and platforms
|
||||
test-all:
|
||||
BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov tests test-int
|
||||
|
||||
# Generate HTML coverage report
|
||||
coverage:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
uv run coverage erase
|
||||
|
||||
echo "🔎 Coverage (SQLite)..."
|
||||
BASIC_MEMORY_ENV=test uv run coverage run --source=basic_memory -m pytest -p pytest_mock -v --no-cov tests test-int
|
||||
|
||||
echo "🔎 Coverage (Postgres via testcontainers)..."
|
||||
# Note: Uses timeout due to FastMCP Client + asyncpg cleanup hang (tests pass, process hangs on exit)
|
||||
# See: https://github.com/jlowin/fastmcp/issues/1311
|
||||
TIMEOUT_CMD=$(command -v gtimeout || command -v timeout || echo "")
|
||||
if [[ -n "$TIMEOUT_CMD" ]]; then
|
||||
$TIMEOUT_CMD --signal=KILL 600 bash -c 'BASIC_MEMORY_ENV=test BASIC_MEMORY_TEST_POSTGRES=1 uv run coverage run --source=basic_memory -m pytest -p pytest_mock -v --no-cov -m postgres tests test-int' || test $? -eq 137
|
||||
else
|
||||
echo "⚠️ No timeout command found, running without timeout..."
|
||||
BASIC_MEMORY_ENV=test BASIC_MEMORY_TEST_POSTGRES=1 uv run coverage run --source=basic_memory -m pytest -p pytest_mock -v --no-cov -m postgres tests test-int
|
||||
fi
|
||||
|
||||
echo "🧩 Combining coverage data..."
|
||||
uv run coverage combine
|
||||
uv run coverage report -m
|
||||
uv run coverage html
|
||||
echo "Coverage report generated in htmlcov/index.html"
|
||||
|
||||
# Lint and fix code (calls fix)
|
||||
lint: fix
|
||||
|
||||
# Lint and fix code
|
||||
fix:
|
||||
uv run ruff check --fix --unsafe-fixes src tests test-int
|
||||
|
||||
# Type check code (pyright)
|
||||
typecheck:
|
||||
uv run pyright
|
||||
|
||||
# Type check code (ty)
|
||||
typecheck-ty:
|
||||
uv run ty check src/
|
||||
|
||||
# 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
|
||||
|
||||
# Run doctor checks in an isolated temp home/config
|
||||
doctor:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
TMP_HOME=$(mktemp -d)
|
||||
TMP_CONFIG=$(mktemp -d)
|
||||
HOME="$TMP_HOME" \
|
||||
BASIC_MEMORY_ENV=test \
|
||||
BASIC_MEMORY_HOME="$TMP_HOME/basic-memory" \
|
||||
BASIC_MEMORY_CONFIG_DIR="$TMP_CONFIG" \
|
||||
./.venv/bin/python -m basic_memory.cli.main doctor --local
|
||||
|
||||
|
||||
# Update all dependencies to latest versions
|
||||
update-deps:
|
||||
uv sync --upgrade
|
||||
|
||||
# Run all code quality checks and tests
|
||||
check: lint format typecheck test
|
||||
|
||||
# Run all code quality checks and all test suites, including semantic benchmarks
|
||||
check-all: lint format typecheck test test-semantic
|
||||
|
||||
# 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 lint checks..."
|
||||
just lint
|
||||
just typecheck
|
||||
|
||||
# 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
|
||||
|
||||
# Update version in server.json (MCP registry metadata)
|
||||
echo "📝 Updating version in server.json..."
|
||||
sed -i.bak "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION_NUM\"/g" server.json
|
||||
rm -f server.json.bak
|
||||
|
||||
# Commit version update
|
||||
git add src/basic_memory/__init__.py server.json
|
||||
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"
|
||||
echo ""
|
||||
echo "📝 REMINDER: Post-release tasks:"
|
||||
echo " 1. docs.basicmemory.com - Add release notes to src/pages/latest-releases.mdx"
|
||||
echo " 2. basicmachines.co - Update version in src/components/sections/hero.tsx"
|
||||
echo " 3. MCP Registry - Run: mcp-publisher publish"
|
||||
echo " See: .claude/commands/release/release.md for detailed instructions"
|
||||
|
||||
# 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 lint checks..."
|
||||
just lint
|
||||
just typecheck
|
||||
|
||||
# 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
|
||||
|
||||
# Update version in server.json (MCP registry metadata)
|
||||
echo "📝 Updating version in server.json..."
|
||||
sed -i.bak "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION_NUM\"/g" server.json
|
||||
rm -f server.json.bak
|
||||
|
||||
# Commit version update
|
||||
git add src/basic_memory/__init__.py server.json
|
||||
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"
|
||||
echo ""
|
||||
echo "📝 REMINDER: For stable releases, update documentation sites:"
|
||||
echo " 1. docs.basicmemory.com - Add release notes to src/pages/latest-releases.mdx"
|
||||
echo " 2. basicmachines.co - Update version in src/components/sections/hero.tsx"
|
||||
echo " See: .claude/commands/release/release.md for detailed instructions"
|
||||
|
||||
# List all available recipes
|
||||
default:
|
||||
@just --list
|
||||