Compare commits

..

37 Commits

Author SHA1 Message Date
Joe P 73cade27ab chore: update version to 0.14.4 for v0.14.4 release 2025-09-04 14:03:09 -06:00
Joe P 7e024a8674 fix: resolve linting errors for release preparation
- Replace bare except clauses with Exception in legal_file_inventory.py
- Remove unused variables in test files
- Prepare codebase for v0.14.4 release
2025-09-04 14:01:06 -06:00
Drew Cain 22f7bfa398 fix: Update YAML frontmatter tag formatting for Obsidian compatibility (#280)
Signed-off-by: Drew Cain <groksrc@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Paul Hernandez <60959+phernandez@users.noreply.github.com>
2025-09-04 09:57:45 -05:00
jope-bm cd7cee650f fix: complete project management special character support (#272) (#279)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-29 10:57:13 -06:00
Paul Hernandez 105bcaa025 feat: implement non-root Docker container to fix file ownership issues (#277)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Drew Cain <groksrc@gmail.com>
2025-08-28 22:15:14 -05:00
Brandon Mayes 74e12eb782 fix: Sanitize filenames and allow optional kebab case (#260)
Signed-off-by: Brandon Mayes <5610870+bdmayes@users.noreply.github.com>
2025-08-27 19:29:01 -04:00
Drew Cain 7a8b08d11e fix: Windows test failures and add Windows CI support (#273)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-25 08:58:24 -05:00
manuelbliemel 9aa40246a8 Addressed issues when running basic-memory on the Windows platform (#252)
Signed-off-by: Manuel Bliemel <manuel.bliemel@gmail.com>
2025-08-24 19:12:40 -07:00
jope-bm 7aff836c57 fix: Add ISO datetime serialization to MCP schema models (#270)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-23 07:23:07 -06:00
jope-bm 285e96baea fix: Fix observation parsing to exclude markdown and wiki links (#269)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-22 20:16:05 -06:00
jope-bm 2cd2a62f30 fix: Ensure all datetime operations return timezone-aware objects (#268)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-22 13:43:55 -06:00
jope-bm f3d8d8d617 fix: Use discriminated unions for MCP schema validation in build_context (#266)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
2025-08-22 09:36:32 -06:00
jope-bm 9743fcd13e fix: Respect BASIC_MEMORY_LOG_LEVEL and BASIC_MEMORY_CONSOLE_LOGGING environment variables (#264)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-22 07:58:19 -06:00
phernandez 65d1984a53 Update CLA.md to include copyright and license info
Signed-off-by: phernandez <paul@basicmachines.co>
2025-08-21 18:21:24 -05:00
jope-bm b814d40ab1 fix: Add project isolation to ContextService.find_related() method (#261) (#262)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 20:07:04 -06:00
Paul Hernandez 2438094914 fix: handle vim atomic write DELETE events without ADD (#249)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
Co-authored-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 14:36:43 -05:00
jope-bm 5d74d7407c fix: Enable string-to-integer conversion for build_context depth parameter (#259)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 11:22:14 -06:00
jope-bm b6aeb3217c fix: Add missing foreign key constraints for project removal (#254) (#258)
Signed-off-by: Joe P <joe@basicmemory.com>
Signed-off-by: joe@basicmemory.com
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-20 08:49:07 -06:00
jope-bm 08ee7e1201 fix: Critical search index bug - prevent note disappearing on edit (#257)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
2025-08-19 15:41:27 -06:00
phernandez 63ae9ee0e4 docs: Re-implement external documentation improvements
- Fix typo: 'enviroment' -> 'environment' in CLAUDE.md
- Update HTTP links to HTTPS in README.md
- Add comprehensive VS Code integration instructions
- Maintain correct internal link references

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

Will be re-implemented by Basic Machines team for clean IP ownership.
2025-08-08 15:26:29 -05:00
phernandez 59eae34dee fix: Update function name in error messages to use correct search_notes
Corrects error message templates to reference the actual search_notes function name for consistency.
2025-08-08 15:25:33 -05:00
phernandez b1e55e169e revert: Remove external function name fix for clean IP
Original contribution by Amadeusz Wieczorek will be re-implemented by Basic Machines team.
2025-08-08 15:24:58 -05:00
phernandez 173bff35c1 feat: Add Chinese character support to permalink generation
Preserves non-ASCII characters like Chinese in permalinks while maintaining
backward compatibility with ASCII-only processing. This re-implements
functionality that was contributed externally, now with Basic Machines authorship.
2025-08-08 15:24:23 -05:00
phernandez 629c8e47c9 revert: Remove external Chinese character fix for clean IP
Original contribution by andyxinweiminicloud will be re-implemented by Basic Machines team for clean IP ownership.
2025-08-08 15:23:28 -05:00
phernandez 9e4b8bca8f Add legal inventory documentation for IP analysis 2025-08-08 15:16:39 -05:00
Drew Cain b0cc559426 chore: update version to 0.14.3 for v0.14.3 release 2025-08-01 22:06:53 -05:00
Drew Cain 7460a938df fix: make case sensitivity test platform-aware
- Add platform detection to handle case-insensitive file systems
- Test now passes on macOS and Windows while maintaining Linux behavior
- Fixes test failure on case-insensitive file systems
2025-08-01 22:02:53 -05:00
Drew Cain 43fa5762a8 ruff checks
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-08-01 21:50:18 -05:00
Paul Hernandez fb1350b294 fix: enhance character conflict detection and error handling for sync operations (#201)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-08-01 21:35:56 -05:00
Paul Hernandez 7585a29c96 fix: replace recursive _traverse_messages with iterative approach to handle deep conversation threads (#235)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
2025-08-01 21:34:11 -05:00
Drew Cain 752c78c379 chore: minor cleanup (#228)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2025-07-31 21:32:56 -05:00
jope-bm a4a3b1b689 fix: handle missing 'name' key in memory JSON import (#241)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
2025-07-28 14:52:15 -06:00
jope-bm 6361574a20 fix: basic memory home env var not respected when project path is changed. (#239)
Signed-off-by: Joe P <joe@basicmemory.com>
2025-07-28 14:50:47 -06:00
jope-bm 24a1d6195d fix: path traversal security vulnerability in mcp tools (#223)
Signed-off-by: Joe P <joe@basicmemory.com>
2025-07-15 09:05:11 -06:00
jope-bm a0cf62375d docs: improve virtual environment setup instructions (#222)
Co-authored-by: Claude <noreply@anthropic.com>
2025-07-10 10:18:43 -06:00
Paul Hernandez 473f70c949 chore: Cloud auth (#213)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude <noreply@anthropic.com>
2025-07-07 21:08:25 -05:00
151 changed files with 25840 additions and 4135 deletions
+12 -3
View File
@@ -14,11 +14,12 @@ on:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
python-version: [ "3.12" ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -35,10 +36,18 @@ jobs:
run: |
pip install uv
- name: Install just
- name: Install just (Linux/macOS)
if: runner.os != 'Windows'
run: |
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
- name: Install just (Windows)
if: runner.os == 'Windows'
run: |
# Install just using Chocolatey (pre-installed on GitHub Actions Windows runners)
choco install just --yes
shell: pwsh
- name: Create virtual env
run: |
uv venv
@@ -54,4 +63,4 @@ jobs:
- name: Run tests
run: |
uv pip install pytest pytest-cov
just test
just test
+63 -26
View File
@@ -1,34 +1,71 @@
Developer Certificate of Origin
Version 1.1
https://developercertificate.org/
# Contributor License Agreement
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
## Copyright Assignment and License Grant
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
By signing this Contributor License Agreement ("Agreement"), you accept and agree to the following terms and conditions
for your present and future Contributions submitted
to Basic Machines LLC. Except for the license granted herein to Basic Machines LLC and recipients of software
distributed by Basic Machines LLC, you reserve all right,
title, and interest in and to your Contributions.
Developer's Certificate of Origin 1.1
### 1. Definitions
By making a contribution to this project, I certify that:
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this
Agreement with Basic Machines LLC.
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work,
that is intentionally submitted by You to Basic
Machines LLC for inclusion in, or documentation of, any of the products owned or managed by Basic Machines LLC (the "
Work").
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
### 2. Grant of Copyright License
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
software distributed by Basic Machines LLC a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Work, and to permit persons to whom the Work is furnished to do so.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
### 3. Assignment of Copyright
You hereby assign to Basic Machines LLC all right, title, and interest worldwide in all Copyright covering your
Contributions. Basic Machines LLC may license the
Contributions under any license terms, including copyleft, permissive, commercial, or proprietary licenses.
### 4. Grant of Patent License
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
software distributed by Basic Machines LLC a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to
make, have made, use, offer to sell, sell, import, and
otherwise transfer the Work.
### 5. Developer Certificate of Origin
By making a Contribution to this project, You certify that:
(a) The Contribution was created in whole or in part by You and You have the right to submit it under this Agreement; or
(b) The Contribution is based upon previous work that, to the best of Your knowledge, is covered under an appropriate
open source license and You have the right under that
license to submit that work with modifications, whether created in whole or in part by You, under this Agreement; or
(c) The Contribution was provided directly to You by some other person who certified (a), (b) or (c) and You have not
modified it.
(d) You understand and agree that this project and the Contribution are public and that a record of the Contribution (
including all personal information You submit with
it, including Your sign-off) is maintained indefinitely and may be redistributed consistent with this project or the
open source license(s) involved.
### 6. Representations
You represent that you are legally entitled to grant the above license and assignment. If your employer(s) has rights to
intellectual property that you create that
includes your Contributions, you represent that you have received permission to make Contributions on behalf of that
employer, or that your employer has waived such rights
for your Contributions to Basic Machines LLC.
---
This Agreement is effective as of the date you first submit a Contribution to Basic Machines LLC.
+32 -118
View File
@@ -14,15 +14,15 @@ See the [README.md](README.md) file for a project overview.
### Build and Test Commands
- Install: `just install` or `pip install -e ".[dev]"`
- Run tests: `uv run pytest -p pytest_mock -v` or `just test`
- 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: `just lint` or `ruff check . --fix`
- Type check: `just type-check` or `uv run pyright`
- Format: `just format` or `uv run ruff format .`
- Run all code checks: `just check` (runs lint, format, type-check, test)
- Create db migration: `just migration "Your migration message"`
- Run development MCP Inspector: `just run-inspector`
- 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
@@ -37,7 +37,6 @@ See the [README.md](README.md) file for a project overview.
- API uses FastAPI for endpoints
- Follow the repository pattern for data access
- Tools communicate to api routers via the httpx ASGI client (in process)
- avoid using "private" functions in modules or classes (prepended with _)
### Codebase Architecture
@@ -65,7 +64,6 @@ See the [README.md](README.md) file for a project overview.
- Test database uses in-memory SQLite
- Avoid creating mocks in tests in most circumstances.
- Each test runs in a standalone environment with in memory SQLite and tmp_file directory
- Do not use mocks in tests if possible. Tests run with an in memory sqlite db, so they are not needed. See fixtures in conftest.py
## BASIC MEMORY PRODUCT USAGE
@@ -97,29 +95,18 @@ See the [README.md](README.md) file for a project overview.
**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
- `edit_note(identifier, operation, content)` - Edit notes incrementally (append, prepend, find/replace, section replace)
- `move_note(identifier, destination_path)` - Move notes with database consistency and search reindexing
- `view_note(identifier)` - Display notes as formatted artifacts for better readability in Claude Desktop
- `read_content(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
- `delete_note(identifier)` - Delete notes from knowledge base
**Project Management:**
- `list_memory_projects()` - List all available projects with status indicators
- `switch_project(project_name)` - Switch to different project context during conversations
- `get_current_project()` - Show currently active project with statistics
- `create_memory_project(name, path, set_default)` - Create new Basic Memory projects
- `delete_project(name)` - Delete projects from configuration and database
- `set_default_project(name)` - Set default project in config
- `sync_status()` - Check file synchronization status and background operations
- `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")
- `list_directory(dir_name, depth, file_name_glob)` - List directory contents with filtering and depth control
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation
continuity
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "
1d", "1 week")
**Search & Discovery:**
- `search_notes(query, page, page_size)` - Full-text search across all content with filtering options
- `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
@@ -127,7 +114,7 @@ See the [README.md](README.md) file for a project overview.
- MCP Prompts for better AI interaction:
- `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
- `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
- `search_notes(query, after_date)` - Search with detailed, formatted results for better context understanding
- `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
@@ -147,32 +134,30 @@ could achieve independently.
## GitHub Integration
Basic Memory uses Claude directly into the development workflow through GitHub:
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:
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
- 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
- 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
- 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.
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
@@ -183,75 +168,4 @@ With GitHub integration, the development workflow includes:
3. **Branch management** - Claude can create feature branches for implementations
4. **Documentation maintenance** - Claude can keep documentation updated as the code evolves
With this integration, the AI assistant is a full-fledged team member rather than just a tool for generating code
snippets.
### Basic Memory Pro
Basic Memory Pro is a desktop GUI application that wraps the basic-memory CLI/MCP tools:
- Built with Tauri (Rust), React (TypeScript), and a Python FastAPI sidecar
- Provides visual knowledge graph exploration and project management
- Uses the same core codebase but adds a desktop-friendly interface
- Project configuration is shared between CLI and Pro versions
- Multiple project support with visual switching interface
local repo: /Users/phernandez/dev/basicmachines/basic-memory-pro
github: https://github.com/basicmachines-co/basic-memory-pro
## Release and Version Management
Basic Memory uses `uv-dynamic-versioning` for automatic version management based on git tags:
### Version Types
- **Development versions**: Automatically generated from commits (e.g., `0.12.4.dev26+468a22f`)
- **Beta releases**: Created by tagging with beta suffixes (e.g., `v0.13.0b1`, `v0.13.0rc1`)
- **Stable releases**: Created by tagging with version numbers (e.g., `v0.13.0`)
### Release Workflows
#### Development Builds (Automatic)
- Triggered on every push to `main` branch
- Publishes dev versions like `0.12.4.dev26+468a22f` to PyPI
- Allows continuous testing of latest changes
- Users install with: `pip install basic-memory --pre --force-reinstall`
#### Beta/RC Releases (Manual)
- Create beta tag: `git tag v0.13.0b1 && git push origin v0.13.0b1`
- Automatically builds and publishes to PyPI as pre-release
- Users install with: `pip install basic-memory --pre`
- Use for milestone testing before stable release
#### Stable Releases (Automated)
- Use the automated release system: `just release v0.13.0`
- Includes comprehensive quality checks (lint, format, type-check, tests)
- Automatically updates version in `__init__.py`
- Creates git tag and pushes to GitHub
- Triggers GitHub Actions workflow for:
- PyPI publication
- Homebrew formula update (requires HOMEBREW_TOKEN secret)
**Manual method (legacy):**
- Create version tag: `git tag v0.13.0 && git push origin v0.13.0`
#### Homebrew Formula Updates
- Automatically triggered after successful PyPI release for **stable releases only**
- **Stable releases** (e.g., v0.13.7) automatically update the main `basic-memory` formula
- **Pre-releases** (dev/beta/rc) are NOT automatically updated - users must specify version manually
- Updates formula in `basicmachines-co/homebrew-basic-memory` repo
- Requires `HOMEBREW_TOKEN` secret in GitHub repository settings:
- Create a fine-grained Personal Access Token with `Contents: Read and Write` and `Actions: Read` scopes on `basicmachines-co/homebrew-basic-memory`
- Add as repository secret named `HOMEBREW_TOKEN` in `basicmachines-co/basic-memory`
- Formula updates include new version URL and SHA256 checksum
### For Development
- **Automated releases**: Use `just release v0.13.x` for stable releases and `just beta v0.13.0b1` for beta releases
- **Quality gates**: All releases require passing lint, format, type-check, and test suites
- **Version management**: Versions automatically derived from git tags via `uv-dynamic-versioning`
- **Configuration**: `pyproject.toml` uses `dynamic = ["version"]`
- **Release automation**: `__init__.py` updated automatically during release process
- **CI/CD**: GitHub Actions handles building and PyPI publication
## Development Notes
- make sure you sign off on commits
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.
+6 -1
View File
@@ -27,7 +27,12 @@ project and how to get started as a developer.
> **Note**: Basic Memory uses [just](https://just.systems) as a modern command runner. Install with `brew install just` or `cargo install just`.
3. **Run the Tests**:
3. **Activate the Virtual Environment**
```bash
source .venv/bin/activate
```
4. **Run the Tests**:
```bash
# Run all tests
just test
+15 -2
View File
@@ -1,5 +1,9 @@
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/
@@ -7,6 +11,11 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# Create a group and user with the provided UID/GID
# Check if the GID already exists, if not create appgroup
RUN (getent group ${GID} || groupadd --gid ${GID} appgroup) && \
useradd --uid ${UID} --gid ${GID} --create-home --shell /bin/bash appuser
# Copy the project into the image
ADD . /app
@@ -14,13 +23,17 @@ ADD . /app
WORKDIR /app
RUN uv sync --locked
# Create data directory
RUN mkdir -p /app/data
# Create necessary directories and set ownership
RUN mkdir -p /app/data /app/.basic-memory && \
chown -R appuser:${GID} /app
# Set default data directory and add venv to PATH
ENV BASIC_MEMORY_HOME=/app/data \
PATH="/app/.venv/bin:$PATH"
# Switch to the non-root user
USER appuser
# Expose port
EXPOSE 8000
+250
View File
@@ -0,0 +1,250 @@
# Legal File Inventory Guide
## Overview
This guide documents the comprehensive file inventory script created for Basic Memory's legal documentation needs, including copyright assignments and company agreement exhibits.
## Repository Analysis Summary
### Repository Structure
- **Primary Language**: Python (3,958 files)
- **License**: GNU Affero General Public License v3.0
- **Total Contributors**: 12 identified contributors
- **Main Contributors**:
- `phernandez@basicmachines.co` / `paul@basicmachines.co` (Paul Hernandez) - 700+ commits
- `groksrc@gmail.com` / `groksrc@users.noreply.github.com` (Drew Cain) - 18+ commits
- Various AI bots and automated systems
### File Type Distribution
Based on the analysis, the repository contains:
| Extension | Count | Purpose |
|-----------|-------|---------|
| `.py` | 3,958 | Python source code |
| `.pyi` | 4,536 | Python type stubs (dependencies) |
| `.pyc` | 1,435 | Compiled Python (excluded from inventory) |
| `.md` | 34 | Documentation files |
| `.toml` | 179 | Configuration files |
| `.html` | 119 | Coverage reports and documentation |
| `.txt` | 127 | Various text files |
### Contributors by Email Domain
**Basic Machines Contributors:**
- `phernandez@basicmachines.co` - 547 commits (Primary maintainer)
- `paul@basicmachines.co` / `paulmh@gmail.com` - 170 commits (Paul Hernandez)
- `claude@basicmachines.co` - 3 commits (AI assistant)
**External Contributors:**
- `groksrc@gmail.com` / `groksrc@users.noreply.github.com` - 18 commits (Drew Cain)
- Various one-time contributors (1-2 commits each)
**Automated Systems:**
- GitHub Actions, semantic-release, and other bots
## Legal Inventory Script Features
### What It Includes
**Source Files:**
- All Python source code (`.py` files)
- Configuration files (`.toml`, `.yaml`, `.json`, etc.)
- Documentation (`.md`, `.rst`, `.txt`)
- Build and deployment scripts
- Database migrations and SQL files
- Legal and license files
**Metadata for Each File:**
- File path, size, and creation/modification dates
- Git history (creation date, last modified, commit count)
- Contributors and their line contributions
- Primary author identification
- File categorization
- SHA-256 hash for integrity verification
### What It Excludes
**Generated/Build Artifacts:**
- `__pycache__/` and `.pyc` files
- Build directories (`build/`, `dist/`, `htmlcov/`)
- Coverage reports and cache files
**Dependencies:**
- Virtual environment files (`.venv/`, `venv/`)
- Third-party packages (`site-packages/`, `*.dist-info/`)
- Lock files (`uv.lock`, `package-lock.json`)
**IDE/Editor Files:**
- `.idea/`, `.vscode/`, `.DS_Store`
- Temporary and swap files
**Version Control:**
- `.git/` directory contents
- Git configuration files
## Usage Instructions
### Basic Usage
```bash
# Generate CSV inventory (default)
python3 legal_file_inventory.py
# Generate Markdown report
python3 legal_file_inventory.py --format markdown --output legal_report.md
# Generate JSON with full metadata
python3 legal_file_inventory.py --format json --output legal_data.json
# Specify different repository path
python3 legal_file_inventory.py --repo-path /path/to/repo --output inventory.csv
```
### Command Line Options
- `--output`, `-o`: Output file path (default: `basic_memory_legal_inventory.csv`)
- `--format`, `-f`: Output format - `csv`, `json`, or `markdown` (default: `csv`)
- `--repo-path`, `-r`: Repository path (default: current directory)
### Output Formats
**CSV Format:**
- Suitable for spreadsheet applications
- Contains all metadata fields
- Contributors stored as JSON string in separate column
**JSON Format:**
- Complete structured data
- Includes summary statistics and detailed file information
- Best for programmatic processing
**Markdown Format:**
- Human-readable report
- Summary statistics and contributor rankings
- Detailed table of all files
## Legal Documentation Applications
### Copyright Assignment Use Cases
1. **Contributor Identification**: The script identifies all contributors to each file based on git blame analysis
2. **Primary Author Recognition**: Determines the primary author of each file (contributor with most lines)
3. **Contribution Metrics**: Provides line counts and commit counts per contributor
4. **File Categorization**: Groups files by purpose (source code, documentation, configuration, etc.)
### Company Agreement Exhibits
The inventory provides comprehensive documentation of:
1. **Intellectual Property Scope**: All source code files and their origins
2. **Contributor Tracking**: Complete list of all individuals who have contributed code
3. **File Integrity**: SHA-256 hashes for verification of file contents
4. **Historical Documentation**: Git creation dates and modification history
### Due Diligence Documentation
The script generates data suitable for:
1. **Legal Review**: Comprehensive file listing with contributor information
2. **IP Audit**: Identification of all copyright holders
3. **License Compliance**: Verification of file ownership and licensing
4. **Asset Documentation**: Complete inventory of company code assets
## Integration with Legal Processes
### Recommended Workflow
1. **Generate Inventory**: Run the script to create current file inventory
2. **Legal Review**: Have legal counsel review the contributor list and file categorization
3. **Copyright Assignment**: Use contributor data to ensure proper copyright assignments
4. **Document Attachment**: Include inventory as exhibit in company agreements
5. **Regular Updates**: Re-run inventory for significant releases or legal milestones
### Key Legal Considerations
**AGPL-3.0 License:**
- All files are under AGPL-3.0 unless otherwise specified
- Contributors retain copyright but license under AGPL-3.0 terms
- Company needs proper copyright assignments for proprietary licensing
**Contributor Rights:**
- External contributors may retain rights to their contributions
- Proper contributor license agreements (CLAs) should be in place
- AI-generated content may have different legal status
**File Categories for Legal Review:**
- **Source Code**: Core IP, requires copyright assignment
- **Configuration**: May contain proprietary deployment information
- **Documentation**: Usually less sensitive but may contain trade secrets
- **Legal/License**: Critical for compliance verification
## Maintenance and Updates
### When to Regenerate Inventory
- Before major releases
- During legal document preparation
- After significant contributor additions
- For annual compliance reviews
- During acquisition or investment processes
### Validation Steps
1. Verify contributor email mapping is accurate
2. Check that file categorization makes sense
3. Ensure excluded files are appropriate
4. Review contributor counts against expectations
5. Validate file hashes for integrity
## Technical Implementation Notes
### Git Integration
- Uses `git blame --line-porcelain` for detailed contributor analysis
- Tracks file history with `git log --follow`
- Handles renamed files and complex git histories
### Performance Considerations
- Scans repository incrementally to handle large codebases
- Excludes binary dependencies to reduce processing time
- Caches git operations where possible
### Error Handling
- Gracefully handles files not in git
- Continues processing if individual files fail
- Provides detailed error reporting
## Security and Privacy
### Data Sensitivity
- Contains contributor names and email addresses
- May reveal internal file structure and organization
- Should be treated as confidential legal documentation
### Recommended Handling
- Limit access to legal and executive team
- Store in secure, access-controlled systems
- Consider redacting contributor emails for external sharing
- Regular cleanup of generated inventory files
## Troubleshooting
### Common Issues
**Git Not Available:**
- Ensure git is installed and repository is initialized
- Check that the script is run from within the repository
**Permission Errors:**
- Ensure read access to all repository files
- Check write permissions for output directory
**Large Repository Performance:**
- Consider running on subsets of files for very large repositories
- Use `--repo-path` to target specific subdirectories
**Contributor Mapping Issues:**
- Git usernames may not match real identities
- Consider post-processing to normalize contributor names
- Review .mailmap files for git identity consolidation
This comprehensive legal file inventory system provides the foundation for proper intellectual property documentation and legal compliance for the Basic Memory project.
+19 -90
View File
@@ -13,11 +13,8 @@ Basic Memory lets you build persistent knowledge through natural conversations w
Claude, while keeping everything in simple Markdown files on your computer. It uses the Model Context Protocol (MCP) to
enable any compatible LLM to read and write to your local knowledge base.
- Website: https://basicmemory.com
- Company: https://basicmachines.co
- Website: https://basicmachines.co
- Documentation: https://memory.basicmachines.co
- Discord: https://discord.gg/tyvKNccgqN
- YouTube: https://www.youtube.com/@basicmachines-co
## Pick up your conversation right where you left off
@@ -33,10 +30,6 @@ https://github.com/user-attachments/assets/a55d8238-8dd0-454a-be4c-8860dbbd0ddc
# Install with uv (recommended)
uv tool install basic-memory
# or with Homebrew
brew tap basicmachines-co/basic-memory
brew install basic-memory
# Configure Claude Desktop (edit ~/Library/Application Support/Claude/claude_desktop_config.json)
# Add this to your config:
{
@@ -68,14 +61,8 @@ Memory for Claude Desktop:
npx -y @smithery/cli install @basicmachines-co/basic-memory --client claude
```
This installs and configures Basic Memory without requiring manual edits to the Claude Desktop configuration file. Note: The Smithery installation uses their hosted MCP server, while your data remains stored locally as Markdown files.
### Add to Cursor
Once you have installed Basic Memory revisit this page for the 1-click installer for Cursor:
[![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=basic-memory&config=eyJjb21tYW5kIjoiL1VzZXJzL2RyZXcvLmxvY2FsL2Jpbi91dnggYmFzaWMtbWVtb3J5IG1jcCJ9)
This installs and configures Basic Memory without requiring manual edits to the Claude Desktop configuration file. The
Smithery server hosts the MCP server component, while your data remains stored locally as Markdown files.
### Glama.ai
@@ -166,8 +153,7 @@ The note embeds semantic content and links to other topics via simple Markdown f
3. You see this file on your computer in real time in the current project directory (default `~/$HOME/basic-memory`).
- Realtime sync is enabled by default starting with v0.12.0
- Project switching during conversations is supported starting with v0.13.0
- Realtime sync can be enabled via running `basic-memory sync --watch`
4. In a chat with the LLM, you can reference a topic:
@@ -225,7 +211,7 @@ title: <Entity title>
type: <The type of Entity> (e.g. note)
permalink: <a uri slug>
- <optional metadata> (such as tags)
- <optional metadata> (such as tags)
```
### Observations
@@ -277,13 +263,6 @@ Examples of relations:
```
## Using with VS Code
For one-click installation, click one of the install buttons below...
[![Install with UV in VS Code](https://img.shields.io/badge/VS_Code-UV-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D) [![Install with UV in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-UV-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D&quality=insiders)
You can use Basic Memory with VS Code to easily retrieve and store information while coding. Click the installation buttons above for one-click setup, or follow the manual installation instructions below.
### Manual Installation
Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`.
@@ -313,6 +292,8 @@ Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace
}
```
You can use Basic Memory with VS Code to easily retrieve and store information while coding.
## Using with Claude Desktop
Basic Memory is built using the MCP (Model Context Protocol) and works with the Claude desktop app (https://claude.ai/):
@@ -336,8 +317,7 @@ for OS X):
}
```
If you want to use a specific project (see [Multiple Projects](docs/User%20Guide.md#multiple-projects)), update your
Claude Desktop
If you want to use a specific project (see [Multiple Projects](#multiple-projects) below), update your Claude Desktop
config:
```json
@@ -347,9 +327,9 @@ config:
"command": "uvx",
"args": [
"basic-memory",
"mcp",
"--project",
"your-project-name",
"mcp"
"your-project-name"
]
}
}
@@ -358,27 +338,23 @@ config:
2. Sync your knowledge:
Basic Memory will sync the files in your project in real time if you make manual edits.
```bash
# One-time sync of local knowledge updates
basic-memory sync
# Run realtime sync process (recommended)
basic-memory sync --watch
```
3. In Claude Desktop, the LLM can now use these tools:
```
write_note(title, content, folder, tags) - Create or update notes
read_note(identifier, page, page_size) - Read notes by title or permalink
edit_note(identifier, operation, content) - Edit notes incrementally (append, prepend, find/replace)
move_note(identifier, destination_path) - Move notes with database consistency
view_note(identifier) - Display notes as formatted artifacts for better readability
build_context(url, depth, timeframe) - Navigate knowledge graph via memory:// URLs
search_notes(query, page, page_size) - Search across your knowledge base
search(query, page, page_size) - Search across your knowledge base
recent_activity(type, depth, timeframe) - Find recently updated information
canvas(nodes, edges, title, folder) - Generate knowledge visualizations
list_memory_projects() - List all available projects with status
switch_project(project_name) - Switch to different project context
get_current_project() - Show current project and statistics
create_memory_project(name, path, set_default) - Create new projects
delete_project(name) - Delete projects from configuration
set_default_project(name) - Set default project
sync_status() - Check file synchronization status
```
5. Example prompts to try:
@@ -389,10 +365,6 @@ sync_status() - Check file synchronization status
"Create a canvas visualization of my project components"
"Read my notes on the authentication system"
"What have I been working on in the past week?"
"Switch to my work-notes project"
"List all my available projects"
"Edit my coffee brewing note to add a new technique"
"Move my old meeting notes to the archive folder"
```
## Futher info
@@ -404,49 +376,6 @@ See the [Documentation](https://memory.basicmachines.co/) for more info, includi
- [Managing multiple Projects](https://memory.basicmachines.co/docs/cli-reference#project)
- [Importing data from OpenAI/Claude Projects](https://memory.basicmachines.co/docs/cli-reference#import)
## Installation Options
### Stable Release
```bash
pip install basic-memory
```
### Beta/Pre-releases
```bash
pip install basic-memory --pre
```
### Development Builds
Development versions are automatically published on every commit to main with versions like `0.12.4.dev26+468a22f`:
```bash
pip install basic-memory --pre --force-reinstall
```
### Docker
Run Basic Memory in a container with volume mounting for your Obsidian vault:
```bash
# Clone and start with Docker Compose
git clone https://github.com/basicmachines-co/basic-memory.git
cd basic-memory
# Edit docker-compose.yml to point to your Obsidian vault
# Then start the container
docker-compose up -d
```
Or use Docker directly:
```bash
docker run -d \
--name basic-memory-server \
-v /path/to/your/obsidian-vault:/data/knowledge:rw \
-v basic-memory-config:/root/.basic-memory:rw \
ghcr.io/basicmachines-co/basic-memory:latest
```
See [Docker Setup Guide](docs/Docker.md) for detailed configuration options, multiple project setup, and integration examples.
## License
AGPL-3.0
@@ -464,4 +393,4 @@ and submitting PRs.
</picture>
</a>
Built with ♥️ by Basic Machines
Built with ♥️ by Basic Machines
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
Create CSV exhibits for individual contributors
"""
import json
import csv
from pathlib import Path
def create_csv_exhibits():
"""Create CSV Exhibit A files for each contributor."""
# Read the JSON inventory
inventory_files = list(Path("legal_inventory_main").glob("*.json"))
if not inventory_files:
print("Error: No JSON inventory files found")
return
inventory_file = inventory_files[0] # Use the most recent one
print(f"Using inventory file: {inventory_file}")
with open(inventory_file, 'r') as f:
data = json.load(f)
files = data['files']
# Create output directory
output_dir = Path("legal_exhibits")
output_dir.mkdir(exist_ok=True)
# Contributors we need exhibits for
target_contributors = {
'jope-bm': 'joe_exhibit_a.csv',
'Drew Cain': 'drew_cain_exhibit_a.csv'
}
print("Creating CSV exhibits for contributors...")
for contributor_key, filename in target_contributors.items():
# Find files for this contributor
contributor_files = []
for file_info in files:
# Check if this contributor is listed in the file's contributors
for contrib in file_info.get('contributors', []):
if contributor_key in contrib['name']:
contributor_files.append(file_info)
break
if not contributor_files:
print(f"No files found for {contributor_key}")
continue
# Sort files by path
contributor_files.sort(key=lambda x: x['path'])
# Create CSV file
csv_file = output_dir / filename
with open(csv_file, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = [
'file_path', 'file_name', 'category', 'size_bytes',
'modified_date', 'primary_author', 'all_contributors', 'sha256_hash'
]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for file_info in contributor_files:
contributors_list = '; '.join([c['name'] for c in file_info['contributors']])
writer.writerow({
'file_path': file_info['path'],
'file_name': file_info['name'],
'category': file_info['category'],
'size_bytes': file_info['size_bytes'],
'modified_date': file_info['modified_time'][:10],
'primary_author': file_info['primary_author'],
'all_contributors': contributors_list,
'sha256_hash': file_info['sha256_hash']
})
print(f"Created CSV exhibit for {contributor_key}: {csv_file}")
print(f" - {len(contributor_files)} files")
print(f" - {sum(f['size_bytes'] for f in contributor_files):,} bytes")
if __name__ == '__main__':
create_csv_exhibits()
+208
View File
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""
Create individual Exhibit A files for copyright assignments
"""
import json
from pathlib import Path
from datetime import datetime
def create_individual_exhibits():
"""Create individual Exhibit A files for each contributor."""
# Read the JSON inventory
inventory_file = Path("legal_inventory_main/basic_memory_inventory_20250730_101521.json")
if not inventory_file.exists():
print(f"Error: {inventory_file} not found")
return
with open(inventory_file, 'r') as f:
data = json.load(f)
files = data['files']
# Create output directory
output_dir = Path("legal_exhibits")
output_dir.mkdir(exist_ok=True)
# Contributors we need exhibits for (based on copyright assignments)
target_contributors = {
'jope-bm': 'Joseph "Joe" [Last Name]', # Need to get his full name
'Drew Cain': 'Drew Cain'
}
print("Creating individual contributor exhibits...")
for contributor_key, full_name in target_contributors.items():
# Find files for this contributor
contributor_files = []
for file_info in files:
# Check if this contributor is listed in the file's contributors
for contrib in file_info.get('contributors', []):
if contributor_key in contrib['name']:
contributor_files.append(file_info)
break
if not contributor_files:
print(f"No files found for {contributor_key}")
continue
# Sort files by path
contributor_files.sort(key=lambda x: x['path'])
# Create exhibit markdown
exhibit_content = f"""# Exhibit A - Assigned Works
## Copyright Assignment: {full_name} to Basic Memory LLC
**Date:** [To be filled]
**Assignor:** {full_name}
**Assignee:** Basic Memory LLC
## Summary
- **Total Files:** {len(contributor_files)}
- **Total Size:** {sum(f['size_bytes'] for f in contributor_files):,} bytes
- **Categories:** {', '.join(set(f['category'] for f in contributor_files))}
## Detailed File List
"""
# Group by category
categories = {}
for file_info in contributor_files:
category = file_info['category']
if category not in categories:
categories[category] = []
categories[category].append(file_info)
# Add files by category
for category, category_files in sorted(categories.items()):
exhibit_content += f"### {category.replace('_', ' ').title()}\n\n"
for file_info in category_files:
exhibit_content += f"**{file_info['path']}**\n"
exhibit_content += f"- Size: {file_info['size_bytes']:,} bytes\n"
exhibit_content += f"- Modified: {file_info['modified_time'][:10]}\n"
exhibit_content += f"- Primary Author: {file_info['primary_author']}\n"
# Show all contributors for this file
if len(file_info['contributors']) > 1:
contributors_list = ', '.join([c['name'] for c in file_info['contributors']])
exhibit_content += f"- All Contributors: {contributors_list}\n"
exhibit_content += f"- SHA-256: `{file_info['sha256_hash']}`\n\n"
# Add verification section
exhibit_content += f"""
## Verification
This exhibit lists all files in the Basic Memory repository where {full_name} is identified as a contributor based on git commit history analysis.
**Analysis Date:** {datetime.now().strftime('%Y-%m-%d')}
**Repository State:** Basic Memory main branch
**Method:** Git history analysis via `git log --follow` for each file
## Legal Representation
{full_name} hereby represents and warrants that they are the author of the contributions listed above and have the right to assign copyright in these works to Basic Memory LLC.
---
*This exhibit is attached to and forms part of the Copyright Assignment Agreement between {full_name} and Basic Memory LLC.*
"""
# Write exhibit file
safe_name = contributor_key.replace(' ', '_').replace('-', '_').lower()
exhibit_file = output_dir / f"exhibit_a_{safe_name}.md"
with open(exhibit_file, 'w') as f:
f.write(exhibit_content)
print(f"Created exhibit for {full_name}: {exhibit_file}")
print(f" - {len(contributor_files)} files")
print(f" - {sum(f['size_bytes'] for f in contributor_files):,} bytes")
# Create overall summary exhibit (for Paul's assignment to Basic Machines LLC)
create_overall_summary_exhibit(data, output_dir)
def create_overall_summary_exhibit(data, output_dir):
"""Create overall summary exhibit for Company Agreement."""
files = data['files']
summary = data['summary']
contributors = data['contributors']
summary_content = f"""# Basic Memory Repository - Complete IP Inventory
## For Basic Memory LLC Company Agreement
**Analysis Date:** {summary['scan_date'][:10]}
**Repository:** {summary['repository_path']}
## Executive Summary
- **Total Files:** {summary['total_files']:,}
- **Total Size:** {summary['total_size_bytes']:,} bytes
- **Contributors:** {summary['contributor_count']}
- **Primary Author:** Paul Hernandez ({len(contributors.get('Paul Hernandez', {}).get('files', []))} files)
## File Categories
"""
for category, count in sorted(summary['categories'].items()):
summary_content += f"- **{category.replace('_', ' ').title()}:** {count} files\n"
summary_content += """
## Contributor Summary
"""
for contrib in summary['top_contributors']:
summary_content += f"- **{contrib['name']}** ({contrib['email']}): {contrib['file_count']} files, {contrib['commit_count']} commits\n"
summary_content += """
## Legal Significance
This inventory represents the complete codebase of Basic Memory as licensed from Basic Machines LLC to Basic Memory LLC under the copyright license agreement dated [DATE].
### IP Rights Chain
1. **Paul Hernandez** → Basic Machines LLC (copyright assignment)
2. **Basic Machines LLC** → Basic Memory LLC (15% royalty license)
3. **Co-founders** → Basic Memory LLC (direct copyright assignments)
### Due Diligence Documentation
This comprehensive file inventory serves as:
- **Company Agreement Exhibit:** Original codebase licensed to Basic Memory LLC
- **Acquisition Documentation:** Complete IP inventory for due diligence
- **Copyright Verification:** Establishes chain of title for all repository contents
## Repository Contents by Category
"""
# Add sample files by category (first 10 in each category)
for category in sorted(summary['categories'].keys()):
category_files = [f for f in files if f['category'] == category][:10]
if category_files:
summary_content += f"### {category.replace('_', ' ').title()} (Sample)\n\n"
for file_info in category_files:
summary_content += f"- `{file_info['path']}` ({file_info['size_bytes']:,} bytes)\n"
if len([f for f in files if f['category'] == category]) > 10:
remaining = len([f for f in files if f['category'] == category]) - 10
summary_content += f"- *... and {remaining} more files*\n"
summary_content += "\n"
summary_content += f"""
---
*This inventory was generated automatically from git repository analysis and represents the complete intellectual property foundation of Basic Memory as of {summary['scan_date'][:10]}.*
"""
# Write summary file
summary_file = output_dir / "basic_memory_complete_inventory.md"
with open(summary_file, 'w') as f:
f.write(summary_content)
print(f"Created complete inventory summary: {summary_file}")
if __name__ == '__main__':
create_individual_exhibits()
+47 -16
View File
@@ -15,7 +15,7 @@ Basic Memory provides pre-built Docker images on GitHub Container Registry that
--name basic-memory-server \
-p 8000:8000 \
-v /path/to/your/obsidian-vault:/app/data:rw \
-v basic-memory-config:/root/.basic-memory:rw \
-v basic-memory-config:/app/.basic-memory:rw \
ghcr.io/basicmachines-co/basic-memory:latest
```
@@ -30,7 +30,7 @@ Basic Memory provides pre-built Docker images on GitHub Container Registry that
- "8000:8000"
volumes:
- /path/to/your/obsidian-vault:/app/data:rw
- basic-memory-config:/root/.basic-memory:rw
- basic-memory-config:/app/.basic-memory:rw
environment:
- BASIC_MEMORY_DEFAULT_PROJECT=main
restart: unless-stopped
@@ -67,7 +67,7 @@ docker build -t basic-memory .
docker run -d \
--name basic-memory-server \
-v /path/to/your/obsidian-vault:/app/data:rw \
-v basic-memory-config:/root/.basic-memory:rw \
-v basic-memory-config:/app/.basic-memory:rw \
-e BASIC_MEMORY_DEFAULT_PROJECT=main \
basic-memory
```
@@ -86,11 +86,11 @@ Basic Memory requires several volume mounts for proper operation:
2. **Configuration and Database** (Recommended):
```yaml
- basic-memory-config:/root/.basic-memory:rw
- 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 /root/.basic-memory/config.json after Basic Memory starts.
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
@@ -98,7 +98,7 @@ You can edit the basic-memory config.json file located in the /root/.basic-memor
- /path/to/project2:/app/data/project2:rw
```
You can edit the basic-memory config.json file located in the /root/.basic-memory/config.json
You can edit the basic-memory config.json file located in the /app/.basic-memory/config.json
## CLI Commands via Docker
@@ -123,7 +123,7 @@ When using Docker volumes, you'll need to configure projects to point to your mo
1. **Check current configuration:**
```bash
docker exec basic-memory-server cat /root/.basic-memory/config.json
docker exec basic-memory-server cat /app/.basic-memory/config.json
```
2. **Add a project for your mounted volume:**
@@ -184,16 +184,47 @@ environment:
### Linux/macOS
Ensure your knowledge directories have proper permissions:
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
# Make directories readable/writable
chmod -R 755 /path/to/your/obsidian-vault
# Build with custom UID/GID to match your user
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t basic-memory .
# If using specific user/group
chown -R $USER:$USER /path/to/your/obsidian-vault
# 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:
@@ -217,7 +248,7 @@ When using Docker Desktop on Windows, ensure the directories are shared:
```
2. **Configuration Not Persisting:**
- Use named volumes for `/root/.basic-memory`
- Use named volumes for `/app/.basic-memory`
- Check volume mount permissions
3. **Network Connectivity:**
@@ -243,10 +274,10 @@ docker-compose logs -f basic-memory
## Security Considerations
1. **Docker Security:**
The container runs as root for simplicity. For production, consider additional security measures.
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.
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
@@ -288,7 +319,7 @@ 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 /root`
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/).
+241
View File
@@ -0,0 +1,241 @@
# Character Handling and Conflict Resolution
Basic Memory handles various character encoding scenarios and file naming conventions to provide consistent permalink generation and conflict resolution. This document explains how the system works and how to resolve common character-related issues.
## Overview
Basic Memory uses a sophisticated system to generate permalinks from file paths while maintaining consistency across different operating systems and character encodings. The system normalizes file paths and generates unique permalinks to prevent conflicts.
## Character Normalization Rules
### 1. Permalink Generation
When Basic Memory processes a file path, it applies these normalization rules:
```
Original: "Finance/My Investment Strategy.md"
Permalink: "finance/my-investment-strategy"
```
**Transformation process:**
1. Remove file extension (`.md`)
2. Convert to lowercase (case-insensitive)
3. Replace spaces with hyphens
4. Replace underscores with hyphens
5. Handle international characters (transliteration for Latin, preservation for non-Latin)
6. Convert camelCase to kebab-case
### 2. International Character Support
**Latin characters with diacritics** are transliterated:
- `ø``o` (Søren → soren)
- `ü``u` (Müller → muller)
- `é``e` (Café → cafe)
- `ñ``n` (Niño → nino)
**Non-Latin characters** are preserved:
- Chinese: `中文/测试文档.md``中文/测试文档`
- Japanese: `日本語/文書.md``日本語/文書`
## Common Conflict Scenarios
### 1. Hyphen vs Space Conflicts
**Problem:** Files with existing hyphens conflict with generated permalinks from spaces.
**Example:**
```
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug" (CONFLICT!)
```
**Resolution:** The system automatically resolves this by adding suffixes:
```
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug-1"
```
**Best Practice:** Choose consistent naming conventions within your project.
### 2. Case Sensitivity Conflicts
**Problem:** Different case variations that normalize to the same permalink.
**Example on macOS:**
```
Directory: Finance/investment.md
Directory: finance/investment.md (different on filesystem, same permalink)
```
**Resolution:** Basic Memory detects case conflicts and prevents them during sync operations with helpful error messages.
**Best Practice:** Use consistent casing for directory and file names.
### 3. Character Encoding Conflicts
**Problem:** Different Unicode normalizations of the same logical character.
**Example:**
```
File 1: "café.md" (é as single character)
File 2: "café.md" (e + combining accent)
```
**Resolution:** Basic Memory normalizes Unicode characters using NFD normalization to detect these conflicts.
### 4. Forward Slash Conflicts
**Problem:** Forward slashes in frontmatter or file names interpreted as path separators.
**Example:**
```yaml
---
permalink: finance/investment/strategy
---
```
**Resolution:** Basic Memory validates frontmatter permalinks and warns about path separator conflicts.
## Error Messages and Troubleshooting
### "UNIQUE constraint failed: entity.file_path, entity.project_id"
**Cause:** Two entities trying to use the same file path within a project.
**Common scenarios:**
1. File move operation where destination is already occupied
2. Case sensitivity differences on macOS
3. Character encoding conflicts
4. Concurrent file operations
**Resolution steps:**
1. Check for duplicate file names with different cases
2. Look for files with similar names but different character encodings
3. Rename conflicting files to have unique names
4. Run sync again after resolving conflicts
### "File path conflict detected during move"
**Cause:** Enhanced conflict detection preventing potential database integrity violations.
**What this means:** The system detected that moving a file would create a conflict before attempting the database operation.
**Resolution:** Follow the specific guidance in the error message, which will indicate the type of conflict detected.
## Best Practices
### 1. File Naming Conventions
**Recommended patterns:**
- Use consistent casing (prefer lowercase)
- Use hyphens instead of spaces for multi-word files
- Avoid special characters that could conflict with path separators
- Be consistent with directory structure casing
**Examples:**
```
✅ Good:
- finance/investment-strategy.md
- projects/basic-memory-features.md
- docs/api-reference.md
❌ Problematic:
- Finance/Investment Strategy.md (mixed case, spaces)
- finance/Investment Strategy.md (inconsistent case)
- docs/API/Reference.md (mixed case directories)
```
### 2. Permalink Management
**Custom permalinks in frontmatter:**
```yaml
---
type: knowledge
permalink: custom-permalink-name
---
```
**Guidelines:**
- Use lowercase permalinks
- Use hyphens for word separation
- Avoid path separators unless creating sub-paths
- Ensure uniqueness within your project
### 3. Directory Structure
**Consistent casing:**
```
✅ Good:
finance/
investment-strategies.md
portfolio-management.md
❌ Problematic:
Finance/ (capital F)
investment-strategies.md
finance/ (lowercase f)
portfolio-management.md
```
## Migration and Cleanup
### Identifying Conflicts
Use Basic Memory's built-in conflict detection:
```bash
# Sync will report conflicts
basic-memory sync
# Check sync status for warnings
basic-memory status
```
### Resolving Existing Conflicts
1. **Identify conflicting files** from sync error messages
2. **Choose consistent naming convention** for your project
3. **Rename files** to follow the convention
4. **Re-run sync** to verify resolution
### Bulk Renaming Strategy
For projects with many conflicts:
1. **Backup your project** before making changes
2. **Standardize on lowercase** file and directory names
3. **Replace spaces with hyphens** in file names
4. **Use consistent character encoding** (UTF-8)
5. **Test sync after each batch** of changes
## System Enhancements
### Recent Improvements (v0.13+)
1. **Enhanced conflict detection** before database operations
2. **Improved error messages** with specific resolution guidance
3. **Character normalization utilities** for consistent handling
4. **File swap detection** for complex move scenarios
5. **Proactive conflict warnings** during permalink resolution
### Monitoring and Logging
The system now provides detailed logging for conflict resolution:
```
DEBUG: Detected potential file path conflicts for 'Finance/Investment.md': ['finance/investment.md']
WARNING: File path conflict detected during move: entity_id=123 trying to move from 'old.md' to 'new.md'
```
These logs help identify and resolve conflicts before they cause sync failures.
## Support and Resources
If you encounter character-related conflicts not covered in this guide:
1. **Check the logs** for specific conflict details
2. **Review error messages** for resolution guidance
3. **Report issues** with examples of the conflicting files
4. **Consider the file naming best practices** outlined above
The Basic Memory system is designed to handle most character conflicts automatically while providing clear guidance for manual resolution when needed.
+4 -1
View File
@@ -3,6 +3,9 @@
# Install dependencies
install:
pip install -e ".[dev]"
uv sync
@echo ""
@echo "💡 Remember to activate the virtual environment by running: source .venv/bin/activate"
# Run unit tests in parallel
test-unit:
@@ -179,4 +182,4 @@ beta version:
# List all available recipes
default:
@just --list
@just --list
+31
View File
@@ -0,0 +1,31 @@
file_path,file_name,category,size_bytes,modified_date,primary_author,all_contributors,sha256_hash
.github/workflows/release.yml,release.yml,configuration,2479,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez,163307a99e2f3d317af6ae8927e1537654d88bf574a6958ac0bcd2d4de266b2a
CLAUDE.md,CLAUDE.md,legal,12310,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant); Ikko Eltociear Ashimine,751f10c1a9a9c0ba2054d9c9cd36cf0989d6a347491fd505bb6504ee74b12900
README.md,README.md,documentation,16261,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez; Jason Noble; Marc Baiza; Matias Forbord; bm-claudeai (AI Assistant),2c4b82b30f49ab465617034fda999157450ed168f8e097fe8e9ca7ac65a197a7
justfile,justfile,other,5496,2025-07-30,jope-bm,jope-bm; Paul Hernandez; Drew Cain,a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
pyproject.toml,pyproject.toml,configuration,3279,2025-07-30,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); semantic-release,428a5d64d4555dc7cd2a81b89a3444bdb1d2f3017257fbc9160bd0ae982718e1
src/basic_memory/__init__.py,__init__.py,source_code,256,2025-07-06,Paul Hernandez,Paul Hernandez; Drew Cain; semantic-release,c946490ab9d64957d25df37f721392024916f8a81ed838f70a8e938b1c2637db
src/basic_memory/alembic/env.py,env.py,source_code,2838,2025-07-08,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),e241d987eadfcd5011cbd9ddbecb833d306de87937759d81c08d37d04a6906b0
src/basic_memory/api/routers/utils.py,utils.py,source_code,5159,2025-07-03,Drew Cain,Drew Cain; bm-claudeai (AI Assistant),9e60f57da24e1dc9d64236c26a7a2353003dc618a77fbeb853c494aa3357a57c
src/basic_memory/cli/commands/mcp.py,mcp.py,source_code,2593,2025-07-08,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),3e1be3d27b18ef4743204b7cf8a37fbc2a1a91fc1636c468e08cefcd8597bac6
src/basic_memory/config.py,config.py,source_code,12487,2025-07-30,jope-bm,jope-bm; Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant),86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
src/basic_memory/db.py,db.py,source_code,7428,2025-07-08,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),25e053b0b3f9e8fa823b05fc099001efe8a3a4f6e0f80728e5079ba45aa406d8
src/basic_memory/mcp/server.py,server.py,source_code,1248,2025-07-08,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),0a910cae27d61ea923e90fbe2d4b5c7cacf3b29d3e156f0b7f77872ab973c761
src/basic_memory/mcp/tools/__init__.py,__init__.py,source_code,1619,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez,872b771dd52ec3b763645a2baf6e10a70f049ce3106830a6d3f053b3e09a5fe6
src/basic_memory/mcp/tools/project_management.py,project_management.py,source_code,12944,2025-07-06,Drew Cain,Drew Cain; Paul Hernandez,cdac736d65129f689a85c6b82f8e043bc84a31657ead7a8331771171eeaa860f
src/basic_memory/mcp/tools/search.py,search.py,source_code,15883,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant),8519b0057468c4450eb543a2f561bcd0d7cb96e1ce1b95e948e02b309ba6b7ca
src/basic_memory/repository/entity_repository.py,entity_repository.py,source_code,10405,2025-07-03,Drew Cain,Drew Cain; bm-claudeai (AI Assistant); Paul Hernandez,e2a8d1eba6c8d64bc61d7168df0fe8c29a670858bf9daeac464a0f440273fae0
src/basic_memory/schemas/memory.py,memory.py,source_code,5833,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant),acb4a953a553feca672c4895798a7d948ec51f922f75b66dd7d55716e3bebed3
src/basic_memory/services/entity_service.py,entity_service.py,source_code,30440,2025-07-03,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant),7cd5163eca6b8a0772e838c8c869e478166763cd6a2d745b0b9aa5c25a65baee
src/basic_memory/services/initialization.py,initialization.py,source_code,6715,2025-07-08,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),4dde58b799cf4ac90631e59949b55b3355a93bdf99df0d9c2408c0cea67e10c4
src/basic_memory/templates/prompts/continue_conversation.hbs,continue_conversation.hbs,templates,3076,2025-07-01,Drew Cain,Drew Cain; bm-claudeai (AI Assistant),b6bac31d25c0e52d0909b2273285092f4e31bc219107f92ed511cd407b65e992
src/basic_memory/utils.py,utils.py,source_code,8654,2025-07-30,jope-bm,jope-bm; Paul Hernandez; Drew Cain; andyxinweiminicloud; github-actions[bot] (AI Assistant),111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
tests/mcp/test_tool_edit_note.py,test_tool_edit_note.py,source_code,13503,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez,dd441969aa54b3739f2d9202d3feaf46aee807ec51e01a13ef41eda52743944f
tests/mcp/test_tool_search.py,test_tool_search.py,source_code,10959,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez; github-actions[bot] (AI Assistant),07839cf8e89d74ba05ec1dc55fa842444c1f0cc3d1587823b0ac3458ad89e87c
tests/mcp/test_tool_write_note.py,test_tool_write_note.py,source_code,35136,2025-07-30,jope-bm,jope-bm; Drew Cain; Paul Hernandez,45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
tests/repository/test_entity_repository_upsert.py,test_entity_repository_upsert.py,source_code,16983,2025-07-03,Paul Hernandez,Paul Hernandez; Drew Cain,ee0ecd6f4ff8b31838bd6de6fb1018af437e02813d8315c06f4e20a6b78a0572
tests/schemas/test_schemas.py,test_schemas.py,source_code,16880,2025-07-03,Drew Cain,Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant),2c746a00c340f063c160ace67569955a300a294040949695c79fd4ae124fb611
tests/services/test_entity_service.py,test_entity_service.py,source_code,59278,2025-07-03,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),d7d495756faa10b415d705cf26b69d2efbc5f190a834b6767386ff296629ea71
tests/test_config.py,test_config.py,source_code,3680,2025-07-03,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),6a246431cff739274efda895dfa28f38710289a73e645312f5fa05fce0885ec8
tests/test_db_migration_deduplication.py,test_db_migration_deduplication.py,source_code,6166,2025-07-08,Paul Hernandez,Paul Hernandez; Drew Cain,ab768e061531ca2edcd598c101b5d884e549f90c7a4b305a23713a199d263fbb
uv.lock,uv.lock,other,232710,2025-07-30,Paul Hernandez,Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant),caa1e3f6e66464b884a78cab77df2fe303fa3c0dc573be0196474b6d3c150581
1 file_path file_name category size_bytes modified_date primary_author all_contributors sha256_hash
2 .github/workflows/release.yml release.yml configuration 2479 2025-07-03 Drew Cain Drew Cain; Paul Hernandez 163307a99e2f3d317af6ae8927e1537654d88bf574a6958ac0bcd2d4de266b2a
3 CLAUDE.md CLAUDE.md legal 12310 2025-07-03 Drew Cain Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant); Ikko Eltociear Ashimine 751f10c1a9a9c0ba2054d9c9cd36cf0989d6a347491fd505bb6504ee74b12900
4 README.md README.md documentation 16261 2025-07-03 Drew Cain Drew Cain; Paul Hernandez; Jason Noble; Marc Baiza; Matias Forbord; bm-claudeai (AI Assistant) 2c4b82b30f49ab465617034fda999157450ed168f8e097fe8e9ca7ac65a197a7
5 justfile justfile other 5496 2025-07-30 jope-bm jope-bm; Paul Hernandez; Drew Cain a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
6 pyproject.toml pyproject.toml configuration 3279 2025-07-30 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); semantic-release 428a5d64d4555dc7cd2a81b89a3444bdb1d2f3017257fbc9160bd0ae982718e1
7 src/basic_memory/__init__.py __init__.py source_code 256 2025-07-06 Paul Hernandez Paul Hernandez; Drew Cain; semantic-release c946490ab9d64957d25df37f721392024916f8a81ed838f70a8e938b1c2637db
8 src/basic_memory/alembic/env.py env.py source_code 2838 2025-07-08 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) e241d987eadfcd5011cbd9ddbecb833d306de87937759d81c08d37d04a6906b0
9 src/basic_memory/api/routers/utils.py utils.py source_code 5159 2025-07-03 Drew Cain Drew Cain; bm-claudeai (AI Assistant) 9e60f57da24e1dc9d64236c26a7a2353003dc618a77fbeb853c494aa3357a57c
10 src/basic_memory/cli/commands/mcp.py mcp.py source_code 2593 2025-07-08 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) 3e1be3d27b18ef4743204b7cf8a37fbc2a1a91fc1636c468e08cefcd8597bac6
11 src/basic_memory/config.py config.py source_code 12487 2025-07-30 jope-bm jope-bm; Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant) 86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
12 src/basic_memory/db.py db.py source_code 7428 2025-07-08 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) 25e053b0b3f9e8fa823b05fc099001efe8a3a4f6e0f80728e5079ba45aa406d8
13 src/basic_memory/mcp/server.py server.py source_code 1248 2025-07-08 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) 0a910cae27d61ea923e90fbe2d4b5c7cacf3b29d3e156f0b7f77872ab973c761
14 src/basic_memory/mcp/tools/__init__.py __init__.py source_code 1619 2025-07-03 Drew Cain Drew Cain; Paul Hernandez 872b771dd52ec3b763645a2baf6e10a70f049ce3106830a6d3f053b3e09a5fe6
15 src/basic_memory/mcp/tools/project_management.py project_management.py source_code 12944 2025-07-06 Drew Cain Drew Cain; Paul Hernandez cdac736d65129f689a85c6b82f8e043bc84a31657ead7a8331771171eeaa860f
16 src/basic_memory/mcp/tools/search.py search.py source_code 15883 2025-07-03 Drew Cain Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant) 8519b0057468c4450eb543a2f561bcd0d7cb96e1ce1b95e948e02b309ba6b7ca
17 src/basic_memory/repository/entity_repository.py entity_repository.py source_code 10405 2025-07-03 Drew Cain Drew Cain; bm-claudeai (AI Assistant); Paul Hernandez e2a8d1eba6c8d64bc61d7168df0fe8c29a670858bf9daeac464a0f440273fae0
18 src/basic_memory/schemas/memory.py memory.py source_code 5833 2025-07-03 Drew Cain Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant) acb4a953a553feca672c4895798a7d948ec51f922f75b66dd7d55716e3bebed3
19 src/basic_memory/services/entity_service.py entity_service.py source_code 30440 2025-07-03 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant) 7cd5163eca6b8a0772e838c8c869e478166763cd6a2d745b0b9aa5c25a65baee
20 src/basic_memory/services/initialization.py initialization.py source_code 6715 2025-07-08 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) 4dde58b799cf4ac90631e59949b55b3355a93bdf99df0d9c2408c0cea67e10c4
21 src/basic_memory/templates/prompts/continue_conversation.hbs continue_conversation.hbs templates 3076 2025-07-01 Drew Cain Drew Cain; bm-claudeai (AI Assistant) b6bac31d25c0e52d0909b2273285092f4e31bc219107f92ed511cd407b65e992
22 src/basic_memory/utils.py utils.py source_code 8654 2025-07-30 jope-bm jope-bm; Paul Hernandez; Drew Cain; andyxinweiminicloud; github-actions[bot] (AI Assistant) 111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
23 tests/mcp/test_tool_edit_note.py test_tool_edit_note.py source_code 13503 2025-07-03 Drew Cain Drew Cain; Paul Hernandez dd441969aa54b3739f2d9202d3feaf46aee807ec51e01a13ef41eda52743944f
24 tests/mcp/test_tool_search.py test_tool_search.py source_code 10959 2025-07-03 Drew Cain Drew Cain; Paul Hernandez; github-actions[bot] (AI Assistant) 07839cf8e89d74ba05ec1dc55fa842444c1f0cc3d1587823b0ac3458ad89e87c
25 tests/mcp/test_tool_write_note.py test_tool_write_note.py source_code 35136 2025-07-30 jope-bm jope-bm; Drew Cain; Paul Hernandez 45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
26 tests/repository/test_entity_repository_upsert.py test_entity_repository_upsert.py source_code 16983 2025-07-03 Paul Hernandez Paul Hernandez; Drew Cain ee0ecd6f4ff8b31838bd6de6fb1018af437e02813d8315c06f4e20a6b78a0572
27 tests/schemas/test_schemas.py test_schemas.py source_code 16880 2025-07-03 Drew Cain Drew Cain; Paul Hernandez; bm-claudeai (AI Assistant) 2c746a00c340f063c160ace67569955a300a294040949695c79fd4ae124fb611
28 tests/services/test_entity_service.py test_entity_service.py source_code 59278 2025-07-03 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) d7d495756faa10b415d705cf26b69d2efbc5f190a834b6767386ff296629ea71
29 tests/test_config.py test_config.py source_code 3680 2025-07-03 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) 6a246431cff739274efda895dfa28f38710289a73e645312f5fa05fce0885ec8
30 tests/test_db_migration_deduplication.py test_db_migration_deduplication.py source_code 6166 2025-07-08 Paul Hernandez Paul Hernandez; Drew Cain ab768e061531ca2edcd598c101b5d884e549f90c7a4b305a23713a199d263fbb
31 uv.lock uv.lock other 232710 2025-07-30 Paul Hernandez Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant) caa1e3f6e66464b884a78cab77df2fe303fa3c0dc573be0196474b6d3c150581
+28
View File
@@ -0,0 +1,28 @@
file_path,file_name,category,size_bytes,modified_date,primary_author,all_contributors,sha256_hash
CONTRIBUTING.md,CONTRIBUTING.md,documentation,7329,2025-07-30,jope-bm,jope-bm; Paul Hernandez,ca822fe39cc529bfc3822d317ab8a20fd141ec3ecb8c20de9e2eca3edce1c282
justfile,justfile,other,5496,2025-07-30,jope-bm,jope-bm; Paul Hernandez; Drew Cain,a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
src/basic_memory/api/routers/project_router.py,project_router.py,source_code,8444,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),6539c69f1d506c65d14cc2c151876b19057f37d84d51a9e6f464ed47a4692648
src/basic_memory/cli/commands/import_memory_json.py,import_memory_json.py,source_code,3013,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),7ea193f6720b2672290ad16c79a143a31d88a1d567838f12b9ebf9110a7c0338
src/basic_memory/cli/commands/project.py,project.py,source_code,13836,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),af3e6f30410ab48355f4b1a46aaa3aa5423cb0a0490f51447db1d25e09f39916
src/basic_memory/config.py,config.py,source_code,12487,2025-07-30,jope-bm,jope-bm; Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant),86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
src/basic_memory/importers/memory_json_importer.py,memory_json_importer.py,source_code,4631,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),bc7d045299f17ed9ad5cebff7b1423250ec28a11130e47a0ac48d3ab82bdeafc
src/basic_memory/mcp/async_client.py,async_client.py,source_code,925,2025-07-30,jope-bm,jope-bm; Paul Hernandez,6531f4387f30a30a322197ff51d6eacae4b25a58c648095931b3d1145d1da303
src/basic_memory/mcp/tools/move_note.py,move_note.py,source_code,17541,2025-07-30,jope-bm,jope-bm; Paul Hernandez,5ef27d9baaebc2c5656fc49c4798a4e4e40a70b6cd9d1e761dbcae021bad558c
src/basic_memory/mcp/tools/read_content.py,read_content.py,source_code,9125,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),96a13addac507fd874394dfa2e1e5ed71af5eb89d0872c1d68cacf9cac3f8724
src/basic_memory/mcp/tools/read_note.py,read_note.py,source_code,8132,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant); Amadeusz Wieczorek; github-actions[bot] (AI Assistant),dff53ce02b16d9c5fca315b3f09bb70867a89d614b0ef3f0548dce87978048ee
src/basic_memory/mcp/tools/write_note.py,write_note.py,source_code,6612,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),771396fb49d397feb5f7e36321d4d233c22c4d1b53414912c849957c6e96fc83
src/basic_memory/repository/project_repository.py,project_repository.py,source_code,3803,2025-07-30,jope-bm,jope-bm; bm-claudeai (AI Assistant),9083ba214b2bcaaf98164d9c33cda7add52bc77f7269ecb6e43cdd5088728b7c
src/basic_memory/schemas/importer.py,importer.py,source_code,693,2025-07-30,jope-bm,jope-bm; bm-claudeai (AI Assistant),ac33df423ca32b28ce7b6ea9c29d541f8783a86c0c29f78db35163bf93f139cd
src/basic_memory/services/project_service.py,project_service.py,source_code,30125,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),bce95c00aa9a1da0a8c8090ee34b5b1494207f8c99e6c25de4ec43fddc38f6be
src/basic_memory/utils.py,utils.py,source_code,8654,2025-07-30,jope-bm,jope-bm; Paul Hernandez; Drew Cain; andyxinweiminicloud; github-actions[bot] (AI Assistant),111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
tests/api/test_async_client.py,test_async_client.py,source_code,1382,2025-07-30,jope-bm,jope-bm; Paul Hernandez,a2f96226a75e5cff2326ca48ddccea4915964d83e529f7b99958bc233ccdc31a
tests/api/test_project_router.py,test_project_router.py,source_code,13288,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),af85d0870069d3831a9214372853994e5d12dc910e37298eb9958f72f6a8bb31
tests/cli/test_import_memory_json.py,test_import_memory_json.py,source_code,4891,2025-07-30,jope-bm,jope-bm; bm-claudeai (AI Assistant); Paul Hernandez,d6734ce059dacf97bc764d848b2e2a6ba52fa96871bc331ec9d2ddc60b766bb6
tests/cli/test_project_commands.py,test_project_commands.py,source_code,6578,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),51e2ac7ff44b0d1a1db6da406c5c2e6aebceb1b63a1e8d5ab51bbaa834507157
tests/mcp/test_tool_move_note.py,test_tool_move_note.py,source_code,25851,2025-07-30,jope-bm,jope-bm; Paul Hernandez,c9f95158503fa42e37532aaa35027538fa49cea718d059d570cf547d31f01043
tests/mcp/test_tool_read_content.py,test_tool_read_content.py,source_code,19442,2025-07-30,jope-bm,jope-bm,1817e32f997b06dbdabf02864d0c70584c1cd80bf1fae8dfbef649de79434bc5
tests/mcp/test_tool_read_note.py,test_tool_read_note.py,source_code,22991,2025-07-30,jope-bm,jope-bm; Paul Hernandez; Amadeusz Wieczorek; github-actions[bot] (AI Assistant),4e13ea9ff977525aa62b6a8c22505ffe989329dc3e7d07b71e044cf43ef3ddfd
tests/mcp/test_tool_write_note.py,test_tool_write_note.py,source_code,35136,2025-07-30,jope-bm,jope-bm; Drew Cain; Paul Hernandez,45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
tests/repository/test_project_repository.py,test_project_repository.py,source_code,10511,2025-07-30,jope-bm,jope-bm; bm-claudeai (AI Assistant),279a3324635b1b515c18003bd1875cf230fef1192cadef9ca426846783717e75
tests/services/test_project_service.py,test_project_service.py,source_code,28152,2025-07-30,jope-bm,jope-bm; Paul Hernandez; bm-claudeai (AI Assistant),430868ed796518937a3b0bed4d8c33be53264a6100d61d54b0745bb2b58bf29a
tests/utils/test_validate_project_path.py,test_validate_project_path.py,source_code,15346,2025-07-30,jope-bm,jope-bm,27a4bf2a9d277e6cd773619e5241b20175d211ec60c284361bec404fd99143f3
1 file_path file_name category size_bytes modified_date primary_author all_contributors sha256_hash
2 CONTRIBUTING.md CONTRIBUTING.md documentation 7329 2025-07-30 jope-bm jope-bm; Paul Hernandez ca822fe39cc529bfc3822d317ab8a20fd141ec3ecb8c20de9e2eca3edce1c282
3 justfile justfile other 5496 2025-07-30 jope-bm jope-bm; Paul Hernandez; Drew Cain a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
4 src/basic_memory/api/routers/project_router.py project_router.py source_code 8444 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) 6539c69f1d506c65d14cc2c151876b19057f37d84d51a9e6f464ed47a4692648
5 src/basic_memory/cli/commands/import_memory_json.py import_memory_json.py source_code 3013 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) 7ea193f6720b2672290ad16c79a143a31d88a1d567838f12b9ebf9110a7c0338
6 src/basic_memory/cli/commands/project.py project.py source_code 13836 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) af3e6f30410ab48355f4b1a46aaa3aa5423cb0a0490f51447db1d25e09f39916
7 src/basic_memory/config.py config.py source_code 12487 2025-07-30 jope-bm jope-bm; Paul Hernandez; Drew Cain; bm-claudeai (AI Assistant); github-actions[bot] (AI Assistant) 86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
8 src/basic_memory/importers/memory_json_importer.py memory_json_importer.py source_code 4631 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) bc7d045299f17ed9ad5cebff7b1423250ec28a11130e47a0ac48d3ab82bdeafc
9 src/basic_memory/mcp/async_client.py async_client.py source_code 925 2025-07-30 jope-bm jope-bm; Paul Hernandez 6531f4387f30a30a322197ff51d6eacae4b25a58c648095931b3d1145d1da303
10 src/basic_memory/mcp/tools/move_note.py move_note.py source_code 17541 2025-07-30 jope-bm jope-bm; Paul Hernandez 5ef27d9baaebc2c5656fc49c4798a4e4e40a70b6cd9d1e761dbcae021bad558c
11 src/basic_memory/mcp/tools/read_content.py read_content.py source_code 9125 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) 96a13addac507fd874394dfa2e1e5ed71af5eb89d0872c1d68cacf9cac3f8724
12 src/basic_memory/mcp/tools/read_note.py read_note.py source_code 8132 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant); Amadeusz Wieczorek; github-actions[bot] (AI Assistant) dff53ce02b16d9c5fca315b3f09bb70867a89d614b0ef3f0548dce87978048ee
13 src/basic_memory/mcp/tools/write_note.py write_note.py source_code 6612 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) 771396fb49d397feb5f7e36321d4d233c22c4d1b53414912c849957c6e96fc83
14 src/basic_memory/repository/project_repository.py project_repository.py source_code 3803 2025-07-30 jope-bm jope-bm; bm-claudeai (AI Assistant) 9083ba214b2bcaaf98164d9c33cda7add52bc77f7269ecb6e43cdd5088728b7c
15 src/basic_memory/schemas/importer.py importer.py source_code 693 2025-07-30 jope-bm jope-bm; bm-claudeai (AI Assistant) ac33df423ca32b28ce7b6ea9c29d541f8783a86c0c29f78db35163bf93f139cd
16 src/basic_memory/services/project_service.py project_service.py source_code 30125 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) bce95c00aa9a1da0a8c8090ee34b5b1494207f8c99e6c25de4ec43fddc38f6be
17 src/basic_memory/utils.py utils.py source_code 8654 2025-07-30 jope-bm jope-bm; Paul Hernandez; Drew Cain; andyxinweiminicloud; github-actions[bot] (AI Assistant) 111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
18 tests/api/test_async_client.py test_async_client.py source_code 1382 2025-07-30 jope-bm jope-bm; Paul Hernandez a2f96226a75e5cff2326ca48ddccea4915964d83e529f7b99958bc233ccdc31a
19 tests/api/test_project_router.py test_project_router.py source_code 13288 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) af85d0870069d3831a9214372853994e5d12dc910e37298eb9958f72f6a8bb31
20 tests/cli/test_import_memory_json.py test_import_memory_json.py source_code 4891 2025-07-30 jope-bm jope-bm; bm-claudeai (AI Assistant); Paul Hernandez d6734ce059dacf97bc764d848b2e2a6ba52fa96871bc331ec9d2ddc60b766bb6
21 tests/cli/test_project_commands.py test_project_commands.py source_code 6578 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) 51e2ac7ff44b0d1a1db6da406c5c2e6aebceb1b63a1e8d5ab51bbaa834507157
22 tests/mcp/test_tool_move_note.py test_tool_move_note.py source_code 25851 2025-07-30 jope-bm jope-bm; Paul Hernandez c9f95158503fa42e37532aaa35027538fa49cea718d059d570cf547d31f01043
23 tests/mcp/test_tool_read_content.py test_tool_read_content.py source_code 19442 2025-07-30 jope-bm jope-bm 1817e32f997b06dbdabf02864d0c70584c1cd80bf1fae8dfbef649de79434bc5
24 tests/mcp/test_tool_read_note.py test_tool_read_note.py source_code 22991 2025-07-30 jope-bm jope-bm; Paul Hernandez; Amadeusz Wieczorek; github-actions[bot] (AI Assistant) 4e13ea9ff977525aa62b6a8c22505ffe989329dc3e7d07b71e044cf43ef3ddfd
25 tests/mcp/test_tool_write_note.py test_tool_write_note.py source_code 35136 2025-07-30 jope-bm jope-bm; Drew Cain; Paul Hernandez 45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
26 tests/repository/test_project_repository.py test_project_repository.py source_code 10511 2025-07-30 jope-bm jope-bm; bm-claudeai (AI Assistant) 279a3324635b1b515c18003bd1875cf230fef1192cadef9ca426846783717e75
27 tests/services/test_project_service.py test_project_service.py source_code 28152 2025-07-30 jope-bm jope-bm; Paul Hernandez; bm-claudeai (AI Assistant) 430868ed796518937a3b0bed4d8c33be53264a6100d61d54b0745bb2b58bf29a
28 tests/utils/test_validate_project_path.py test_validate_project_path.py source_code 15346 2025-07-30 jope-bm jope-bm 27a4bf2a9d277e6cd773619e5241b20175d211ec60c284361bec404fd99143f3
+438
View File
@@ -0,0 +1,438 @@
#!/usr/bin/env python3
"""
Legal File Inventory Generator for Basic Memory
This script generates a comprehensive file inventory for legal documentation purposes,
including copyright assignments and company agreement exhibits.
The inventory includes:
- All source code files and their contributors
- Documentation and configuration files
- License and legal files
- Excludes generated files, dependencies, and temporary files
Usage:
python legal_file_inventory.py [--output inventory.csv] [--format csv|json|markdown]
"""
import os
import json
import csv
import subprocess
from pathlib import Path
from datetime import datetime
from typing import Dict, List
import argparse
import hashlib
class FileInventoryGenerator:
def __init__(self, repo_path: str = "."):
self.repo_path = Path(repo_path).resolve()
self.inventory = []
# File patterns to exclude from legal inventory
self.exclude_patterns = {
# Version control and git
'.git', '.gitignore', '.gitmodules',
# Python compiled and cache files
'__pycache__', '*.pyc', '*.pyo', '*.pyd', '.pytest_cache',
# Virtual environments and dependencies
'.venv', 'venv', '.env', 'env', 'ENV',
'*.dist-info', 'site-packages',
# IDE and editor files
'.idea', '.vscode', '*.swp', '*.swo', '.DS_Store',
# Build and distribution artifacts
'build', 'dist', 'htmlcov', '.coverage', '.coverage.*',
'*.egg-info', '.eggs', 'wheels',
# Cache and temporary files
'.ruff_cache', '.mypy_cache', '.tox',
'node_modules', '.npm',
# Documentation build artifacts (but keep source docs)
'.obsidian',
# Lock files (these are generated)
'uv.lock', 'Pipfile.lock', 'poetry.lock', 'package-lock.json'
}
# File extensions that are definitely source/authored content
self.source_extensions = {
'.py', '.md', '.rst', '.txt', '.toml', '.yaml', '.yml',
'.json', '.cfg', '.ini', '.conf', '.sh', '.sql',
'.js', '.ts', '.jsx', '.tsx', '.css', '.scss', '.sass',
'.html', '.htm', '.xml', '.svg', '.dockerfile', '.Dockerfile'
}
# License file patterns
self.license_patterns = {
'LICENSE', 'LICENCE', 'COPYING', 'COPYRIGHT',
'license.txt', 'LICENSE.txt', 'LICENSE.md',
'CITATION.cff', 'CLA.md'
}
def run_git_command(self, command: List[str]) -> str:
"""Run a git command and return the output."""
try:
result = subprocess.run(
['git'] + command,
cwd=self.repo_path,
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError:
return ""
def get_file_contributors(self, file_path: str) -> Dict[str, int]:
"""Get contributors and their line contributions for a file."""
try:
blame_output = self.run_git_command(['blame', '--line-porcelain', file_path])
contributors = {}
for line in blame_output.split('\n'):
if line.startswith('author '):
author = line[7:] # Remove 'author ' prefix
contributors[author] = contributors.get(author, 0) + 1
return contributors
except Exception:
return {}
def get_file_history(self, file_path: str) -> Dict[str, any]:
"""Get file creation date, last modification, and total commits."""
try:
# Get creation date (first commit)
first_commit = self.run_git_command([
'log', '--follow', '--format=%ad', '--date=iso',
'--reverse', file_path
]).split('\n')[0] if self.run_git_command([
'log', '--follow', '--format=%ad', '--date=iso',
'--reverse', file_path
]) else None
# Get last modification date
last_commit = self.run_git_command([
'log', '-1', '--format=%ad', '--date=iso', file_path
])
# Get total commits for this file
commit_count = len(self.run_git_command([
'log', '--follow', '--oneline', file_path
]).split('\n')) if self.run_git_command([
'log', '--follow', '--oneline', file_path
]) else 0
return {
'created': first_commit or 'Unknown',
'last_modified': last_commit or 'Unknown',
'commit_count': commit_count
}
except Exception:
return {
'created': 'Unknown',
'last_modified': 'Unknown',
'commit_count': 0
}
def should_exclude_file(self, file_path: Path) -> bool:
"""Determine if a file should be excluded from the inventory."""
str_path = str(file_path)
# Check if any part of the path matches exclude patterns
for pattern in self.exclude_patterns:
if pattern in str_path or file_path.match(pattern):
return True
# Exclude files in virtual environment paths
if '/.venv/' in str_path or '/venv/' in str_path:
return True
# Exclude binary files that are likely dependencies
if file_path.suffix in {'.so', '.dylib', '.dll', '.pyd'}:
return True
return False
def calculate_file_hash(self, file_path: Path) -> str:
"""Calculate SHA-256 hash of file content."""
try:
with open(file_path, 'rb') as f:
return hashlib.sha256(f.read()).hexdigest()
except Exception:
return ""
def categorize_file(self, file_path: Path) -> str:
"""Categorize the file based on its path and extension."""
str_path = str(file_path).lower()
# License and legal files
if any(pattern.lower() in file_path.name.lower() for pattern in self.license_patterns):
return "Legal/License"
# Documentation
if file_path.suffix.lower() in {'.md', '.rst', '.txt'} and any(
doc_dir in str_path for doc_dir in ['doc', 'readme', 'changelog', 'contributing']
):
return "Documentation"
# Configuration files
if file_path.suffix.lower() in {'.toml', '.yaml', '.yml', '.json', '.cfg', '.ini', '.conf'}:
return "Configuration"
# Source code
if file_path.suffix.lower() in {'.py', '.js', '.ts', '.jsx', '.tsx'}:
return "Source Code"
# Tests
if 'test' in str_path and file_path.suffix.lower() == '.py':
return "Test Code"
# Build and deployment
if file_path.name.lower() in {'dockerfile', 'justfile', 'makefile'} or file_path.suffix.lower() in {'.sh'}:
return "Build/Deployment"
# Database and migrations
if 'migration' in str_path or 'alembic' in str_path or file_path.suffix.lower() == '.sql':
return "Database/Migration"
# Templates and resources
if file_path.suffix.lower() in {'.hbs', '.j2', '.jinja', '.template'}:
return "Templates/Resources"
return "Other"
def scan_repository(self):
"""Scan the repository and build the file inventory."""
print(f"Scanning repository: {self.repo_path}")
for root, dirs, files in os.walk(self.repo_path):
# Skip excluded directories
dirs[:] = [d for d in dirs if not any(pattern in d for pattern in self.exclude_patterns)]
for file in files:
file_path = Path(root) / file
relative_path = file_path.relative_to(self.repo_path)
# Skip excluded files
if self.should_exclude_file(relative_path):
continue
# Get file stats
try:
stat_info = file_path.stat()
file_size = stat_info.st_size
modified_time = datetime.fromtimestamp(stat_info.st_mtime)
except Exception:
file_size = 0
modified_time = datetime.now()
# Get git information
contributors = self.get_file_contributors(str(relative_path))
history = self.get_file_history(str(relative_path))
# Calculate file hash for integrity verification
file_hash = self.calculate_file_hash(file_path)
# Build inventory entry
entry = {
'file_path': str(relative_path),
'full_path': str(file_path),
'file_name': file_path.name,
'file_extension': file_path.suffix,
'file_size_bytes': file_size,
'category': self.categorize_file(relative_path),
'fs_modified_date': modified_time.isoformat(),
'git_created_date': history['created'],
'git_last_modified': history['last_modified'],
'git_commit_count': history['commit_count'],
'contributors': contributors,
'primary_author': max(contributors.items(), key=lambda x: x[1])[0] if contributors else 'Unknown',
'contributor_count': len(contributors),
'total_author_lines': sum(contributors.values()) if contributors else 0,
'sha256_hash': file_hash,
'scan_timestamp': datetime.now().isoformat()
}
self.inventory.append(entry)
print(f"Scanned {len(self.inventory)} files")
def get_summary_statistics(self) -> Dict:
"""Generate summary statistics for the inventory."""
if not self.inventory:
return {}
# Collect all contributors
all_contributors = set()
for entry in self.inventory:
all_contributors.update(entry['contributors'].keys())
# Category breakdown
categories = {}
for entry in self.inventory:
cat = entry['category']
categories[cat] = categories.get(cat, 0) + 1
# File extension breakdown
extensions = {}
for entry in self.inventory:
ext = entry['file_extension'] or 'no_extension'
extensions[ext] = extensions.get(ext, 0) + 1
# Contributor statistics
contributor_files = {}
contributor_lines = {}
for entry in self.inventory:
for contributor, lines in entry['contributors'].items():
contributor_files[contributor] = contributor_files.get(contributor, 0) + 1
contributor_lines[contributor] = contributor_lines.get(contributor, 0) + lines
return {
'total_files': len(self.inventory),
'total_contributors': len(all_contributors),
'categories': categories,
'file_extensions': extensions,
'contributor_file_counts': contributor_files,
'contributor_line_counts': contributor_lines,
'scan_date': datetime.now().isoformat(),
'repository_path': str(self.repo_path)
}
def export_csv(self, output_file: str):
"""Export inventory to CSV format."""
with open(output_file, 'w', newline='', encoding='utf-8') as f:
if not self.inventory:
return
fieldnames = list(self.inventory[0].keys())
# Convert complex fields to strings for CSV
fieldnames = [f for f in fieldnames if f != 'contributors']
fieldnames.append('contributors_json')
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for entry in self.inventory:
row = {k: v for k, v in entry.items() if k != 'contributors'}
row['contributors_json'] = json.dumps(entry['contributors'])
writer.writerow(row)
def export_json(self, output_file: str):
"""Export inventory to JSON format."""
export_data = {
'metadata': self.get_summary_statistics(),
'files': self.inventory
}
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(export_data, f, indent=2, ensure_ascii=False)
def export_markdown(self, output_file: str):
"""Export inventory to Markdown format."""
stats = self.get_summary_statistics()
with open(output_file, 'w', encoding='utf-8') as f:
f.write("# Basic Memory - Legal File Inventory\n\n")
f.write(f"**Generated:** {datetime.now().isoformat()}\n\n")
f.write(f"**Repository:** {stats.get('repository_path', 'Unknown')}\n\n")
# Summary statistics
f.write("## Summary Statistics\n\n")
f.write(f"- **Total Files:** {stats.get('total_files', 0)}\n")
f.write(f"- **Total Contributors:** {stats.get('total_contributors', 0)}\n\n")
# Categories
if 'categories' in stats:
f.write("### Files by Category\n\n")
for category, count in sorted(stats['categories'].items()):
f.write(f"- **{category}:** {count} files\n")
f.write("\n")
# Top contributors
if 'contributor_file_counts' in stats:
f.write("### Top Contributors by Files Modified\n\n")
sorted_contributors = sorted(
stats['contributor_file_counts'].items(),
key=lambda x: x[1],
reverse=True
)[:10]
for contributor, count in sorted_contributors:
f.write(f"- **{contributor}:** {count} files\n")
f.write("\n")
# Detailed file listing
f.write("## Detailed File Inventory\n\n")
f.write("| File Path | Category | Size (bytes) | Primary Author | Contributors |\n")
f.write("|-----------|----------|--------------|----------------|-------------|\n")
for entry in sorted(self.inventory, key=lambda x: x['file_path']):
contributors_str = ', '.join(entry['contributors'].keys())[:50]
if len(contributors_str) == 50:
contributors_str += "..."
f.write(f"| {entry['file_path']} | {entry['category']} | "
f"{entry['file_size_bytes']} | {entry['primary_author']} | "
f"{contributors_str} |\n")
def main():
parser = argparse.ArgumentParser(
description="Generate legal file inventory for Basic Memory repository"
)
parser.add_argument(
'--output', '-o',
default='basic_memory_legal_inventory.csv',
help='Output file path (default: basic_memory_legal_inventory.csv)'
)
parser.add_argument(
'--format', '-f',
choices=['csv', 'json', 'markdown'],
default='csv',
help='Output format (default: csv)'
)
parser.add_argument(
'--repo-path', '-r',
default='.',
help='Path to repository (default: current directory)'
)
args = parser.parse_args()
# Initialize and run the inventory generator
generator = FileInventoryGenerator(args.repo_path)
generator.scan_repository()
# Export in requested format
if args.format == 'csv':
generator.export_csv(args.output)
elif args.format == 'json':
generator.export_json(args.output)
elif args.format == 'markdown':
generator.export_markdown(args.output)
# Print summary
stats = generator.get_summary_statistics()
print("\n=== Legal File Inventory Complete ===")
print(f"Repository: {stats.get('repository_path', 'Unknown')}")
print(f"Total files inventoried: {stats.get('total_files', 0)}")
print(f"Total contributors identified: {stats.get('total_contributors', 0)}")
print(f"Output written to: {args.output}")
# Show top contributors
if 'contributor_file_counts' in stats:
print("\nTop 5 contributors by files modified:")
sorted_contributors = sorted(
stats['contributor_file_counts'].items(),
key=lambda x: x[1],
reverse=True
)[:5]
for i, (contributor, count) in enumerate(sorted_contributors, 1):
print(f" {i}. {contributor}: {count} files")
if __name__ == '__main__':
main()
@@ -0,0 +1,272 @@
path,name,category,size_bytes,modified_time,primary_author,contributor_count,contributors_list,sha256_hash
.claude/commands/release/beta.md,beta.md,documentation,3103,2025-07-01T08:33:22.037438,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),345f06e3f304470a8892e96979355919c8e5d8f4212ebbe543c3272fd15a5cbe
.claude/commands/release/changelog.md,changelog.md,documentation,4648,2025-07-03T18:43:10.075727,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),ea2484352f4f881aa71f546dcf84b0882d60c96673a88406aa2f7e12a282a1f4
.claude/commands/release/release-check.md,release-check.md,documentation,3344,2025-07-01T08:33:22.040060,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),7dd41a40e95c89153936c4ea60a3c989eccd5973fff9f6caef156de82f12bd04
.claude/commands/release/release.md,release.md,documentation,2925,2025-07-01T08:33:22.043674,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),0e4afd788fcf85fbe46094452e9b92a4290c3e23702283e72ee56aa9288476e5
.claude/commands/test-live.md,test-live.md,documentation,18720,2025-07-03T18:43:10.076403,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),21a9edeefb3e5673d833151a2fd0464087c0f0a9d056fdd7fca474c1d177d6fc
.dockerignore,.dockerignore,other,623,2025-07-01T08:33:22.048301,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),b3079bffad89d07186c9166725075524ffc07ceea6dc1d53fdbea6dca9585684
.github/ISSUE_TEMPLATE/bug_report.md,bug_report.md,documentation,967,2025-03-01T14:22:50.968994,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),0d34443ddda84a5e9726c8268083c9646d2cc64ff9c5ebf56c89587cef4c7706
.github/ISSUE_TEMPLATE/config.yml,config.yml,configuration,374,2025-03-01T14:23:26.936836,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),f4e21bb9b7f053509b6c08548a2649a5d62f44f0106810aa8c915d2f29acc623
.github/ISSUE_TEMPLATE/documentation.md,documentation.md,documentation,557,2025-03-01T14:23:40.020117,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),78f80a6b9a2b954695f74a847a068e0b780da6020572401c13c931555a4a9db5
.github/ISSUE_TEMPLATE/feature_request.md,feature_request.md,documentation,786,2025-03-01T14:23:06.344081,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),cee834de442221d53f3d2eeb85b81f3a641b3a7cb587ef8a169452305007439d
.github/dependabot.yml,dependabot.yml,configuration,529,2025-03-15T10:40:16.936258,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),8c6c2ef9a1417da63adb49c70f45524129bd5c46bbd7452b6b97cfe65205f89d
.github/workflows/claude.yml,claude.yml,legal,4628,2025-07-03T18:43:10.076958,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),d026f1756310781b85abc491b454cc31b52dfce4b951cdf8a4a555b6b047604c
.github/workflows/dev-release.yml,dev-release.yml,configuration,1405,2025-06-03T08:31:42.915367,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),58bcfc9e11e210e02dee76aabe7d1378da4edafb7f61857049ee6ccee9d45ffa
.github/workflows/docker.yml,docker.yml,configuration,1621,2025-07-01T08:33:22.049870,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c918a86f2b16ebb45182a73c45272bcd6cea7022cc6e0eaeb2b2a24d8eebfe4d
.github/workflows/pr-title.yml,pr-title.yml,configuration,887,2025-02-17T16:29:35.733646,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),b6df62a20235496feba4d9419c85d6e8446c4d447ad69e7f57b51cc8953f68f4
.github/workflows/release.yml,release.yml,configuration,2479,2025-07-03T18:43:10.077261,Drew Cain,2,Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co),163307a99e2f3d317af6ae8927e1537654d88bf574a6958ac0bcd2d4de266b2a
.github/workflows/test.yml,test.yml,configuration,1426,2025-07-01T08:33:22.054430,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),ab09084a11be8adfc83fc6285c652fbe44a4584aa7d7d3658fcf0c57112d1640
.gitignore,.gitignore,other,566,2025-07-01T08:33:22.055006,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),358658b0faa8c3145e539421deba4ad0bac4a8ea2e608b2c2f867a822c67b36f
.python-version,.python-version,other,5,2024-12-02T16:41:37.012830,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),7b55f8e67b5623c4bef3fa691288da9437d79d3aba156de48d481db32ac7d16d
CHANGELOG.md,CHANGELOG.md,documentation,53647,2025-07-06T10:34:45.550847,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); semantic-release (semantic-release),0b165eb18975ff253cde08f4687b7808ac6ce63b6d8c10912c495a76c9058d1a
CITATION.cff,CITATION.cff,other,302,2025-02-17T16:29:35.735146,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),bc4878cf6f80bb64655f3f93a5821ffe0924ad8311c784b5f81a36093fcffdda
CLA.md,CLA.md,legal,1342,2025-03-01T14:12:38.435132,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),c238c7ff4f2b8451c6bba1bbacd10b04fb7d41d9a9cd76a62644d8193791d169
CLAUDE.md,CLAUDE.md,legal,12310,2025-07-03T18:43:10.078147,Drew Cain,4,Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); Ikko Eltociear Ashimine (eltociear@gmail.com),751f10c1a9a9c0ba2054d9c9cd36cf0989d6a347491fd505bb6504ee74b12900
CODE_OF_CONDUCT.md,CODE_OF_CONDUCT.md,documentation,509,2025-02-17T16:29:35.735238,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),b276b464e85eb1e5d9f09d90bba9573508a7bf81d2eeb38d0f25bd9f2fa66198
CONTRIBUTING.md,CONTRIBUTING.md,documentation,7329,2025-07-30T09:39:36.607158,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co),ca822fe39cc529bfc3822d317ab8a20fd141ec3ecb8c20de9e2eca3edce1c282
Dockerfile,Dockerfile,other,856,2025-07-01T08:33:22.056908,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),c45501905423990e31ffdb8ae4e976eca6423c6784905c1036bcc7795b70feef
LICENSE,LICENSE,legal,34523,2024-12-02T16:41:20.739392,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef
README.md,README.md,documentation,16261,2025-07-03T18:43:10.078281,Drew Cain,6,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Jason Noble (github+jasonn@jasonnoble.org); Marc Baiza (43151891+mbaiza27@users.noreply.github.com); Matias Forbord (codeluggage@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),2c4b82b30f49ab465617034fda999157450ed168f8e097fe8e9ca7ac65a197a7
SECURITY.md,SECURITY.md,documentation,303,2025-03-12T16:04:01.989927,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),96932268b8fe737577941b49e9dff5ff8ad59c11cc3f9a016989589ef5c98c1f
docker-compose.yml,docker-compose.yml,configuration,2534,2025-07-01T08:33:22.059281,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),4a9fdfd2f0d42b148fe021eee77fd5e772c7ba9c2fe5d0cc310c8c7a64257417
docs/AI Assistant Guide.md,AI Assistant Guide.md,documentation,16423,2025-07-01T08:33:22.060491,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c717869a3d356852077026591364ecb0589a6d53f9764af027c57d142ea7ff55
docs/Docker.md,Docker.md,documentation,9088,2025-07-01T08:33:22.060976,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),e13d3f50148353046b52d9d493986880f76f2914ef4efe1a759b2cc9d10913a4
justfile,justfile,other,5496,2025-07-30T09:39:36.608008,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com),a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
llms-install.md,llms-install.md,documentation,2307,2025-06-29T14:19:28.826375,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),674105eb85c8f1d59fc2ddc6840f2a2a3e293358687a305c37dffc8144304f97
memory.json,memory.json,configuration,90075,2025-06-29T14:19:28.827172,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),22d5eee995ca0548b7d9dd152f9a3690c42a514cbf38fea750b3ed2a17dd99c5
pyproject.toml,pyproject.toml,configuration,3279,2025-07-30T09:39:36.608791,Paul Hernandez,4,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); semantic-release (semantic-release),428a5d64d4555dc7cd2a81b89a3444bdb1d2f3017257fbc9160bd0ae982718e1
smithery.yaml,smithery.yaml,configuration,433,2025-03-11T16:36:03.389716,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),63f13499cf4fee0751b5005f5bd85597dc1c7f9a881f4ec8147cc23e95e75c2d
src/basic_memory/__init__.py,__init__.py,source_code,256,2025-07-06T10:34:45.551291,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@gmail.com); semantic-release (semantic-release),c946490ab9d64957d25df37f721392024916f8a81ed838f70a8e938b1c2637db
src/basic_memory/alembic/alembic.ini,alembic.ini,configuration,3733,2025-02-23T17:31:23.198433,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),20466c9c5f026db66793006bebb2f328a3686c7bb34da40d52f33c3eca29e717
src/basic_memory/alembic/env.py,env.py,source_code,2838,2025-07-08T20:39:18.336650,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),e241d987eadfcd5011cbd9ddbecb833d306de87937759d81c08d37d04a6906b0
src/basic_memory/alembic/migrations.py,migrations.py,source_code,718,2025-06-21T00:01:59.613806,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),96b8873d70dd04b48d5c45b7413a54d1224ab9577557ef0dad5929377a9fb144
src/basic_memory/alembic/script.py.mako,script.py.mako,other,635,2025-02-17T16:29:35.739659,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),304a8bfb6a804e5493f5300e79882c70c9f5bb2e87512f4d16f0e097dddd323f
src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py,3dae7c7b1564_initial_schema.py,source_code,4363,2025-06-21T00:01:59.595762,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),9536d69409ddd5eb3bc54f7d0e82607da45ed7f2ad7bc4cbf7cae2a9e28657cd
src/basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py,502b60eaa905_remove_required_from_entity_permalink.py,source_code,1840,2025-06-21T00:01:59.604717,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),93ac584e660f33d468b3eec203b070641298c282bf82655d0beda7f050237681
src/basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py,5fe1ab1ccebe_add_projects_table.py,source_code,4379,2025-06-21T00:01:59.601299,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),d82098f5aca3cdbada60c7229ea4ac262f527349eed9ef9e864509bacfaccf7e
src/basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py,647e7a75e2cd_project_constraint_fix.py,source_code,4210,2025-07-01T08:33:22.063463,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),604ac5908a5976272f52297866d13abb14a99390590935d9fd37cf13e32049fa
src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py,b3c3938bacdb_relation_to_name_unique_index.py,source_code,1428,2025-06-21T00:01:59.598551,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),46c1b2990cdf4575752d234a8b28b494c8a54f15b5360c12f6347aef27b66a92
src/basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py,cc7172b46608_update_search_index_schema.py,source_code,3679,2025-06-21T00:01:59.610909,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),9036af47d42ac7dc12bb93f7a9de1266aff0688b031b550c4e0d92983a24b4c5
src/basic_memory/api/__init__.py,__init__.py,source_code,72,2025-06-21T00:01:59.579981,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),c02a63fb6d63d43d0acca97d425eae9cb055158d0ad6e1a9fc57926512adaa99
src/basic_memory/api/app.py,app.py,source_code,2897,2025-07-08T20:39:18.337081,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ad673ac2c09410af5830f6afdffaa28b0040480dacd52ff01750b8b2c066e8cf
src/basic_memory/api/routers/__init__.py,__init__.py,source_code,398,2025-06-21T00:01:59.549416,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),4443b908a43dea8e6fb4658009cb08c48a56c9b21449e29773cdd159b59cf014
src/basic_memory/api/routers/directory_router.py,directory_router.py,source_code,2054,2025-06-21T00:01:59.567371,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ac1407beec1f7d43a4d222af704b3642559c960bebf2eaf6e1ffe91f7fac5514
src/basic_memory/api/routers/importer_router.py,importer_router.py,source_code,4513,2025-06-21T00:01:59.561600,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),c45502a2b90f5a8f00174c9ad14adc2e65cd7fc0a33d9740a9d750207f0f3bea
src/basic_memory/api/routers/knowledge_router.py,knowledge_router.py,source_code,9738,2025-07-01T08:33:22.064119,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),e1d0ffb4fa5c246582ba844a11b417092de1a173567be55b7060bbc55fbc5681
src/basic_memory/api/routers/management_router.py,management_router.py,source_code,2730,2025-07-08T20:39:18.337438,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),cdbce294dcec6146c56c4dae15744cdf77039fd21b23eef7cbc0b5f9bfb5f4ee
src/basic_memory/api/routers/memory_router.py,memory_router.py,source_code,3003,2025-07-01T08:33:22.064510,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),6bd0a7c775e0c1290efb6001173237c0cdcfa0c1b1bb27c9145a7b35f1596a97
src/basic_memory/api/routers/project_router.py,project_router.py,source_code,8444,2025-07-30T09:39:45.721369,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),6539c69f1d506c65d14cc2c151876b19057f37d84d51a9e6f464ed47a4692648
src/basic_memory/api/routers/prompt_router.py,prompt_router.py,source_code,9948,2025-07-01T08:33:22.065221,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),e30c6aebe35112054933c37d0b462c3750005030fc9e44c23b35b2cd2a93485c
src/basic_memory/api/routers/resource_router.py,resource_router.py,source_code,8058,2025-06-21T00:01:59.570399,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),584244a84698ff24ca8f9f94fab5b891740a51c25f972920c08ebf83f478d5c9
src/basic_memory/api/routers/search_router.py,search_router.py,source_code,1226,2025-06-21T00:01:59.543020,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),183eb68e50904e22ff54db1d89b8be6f57fa1f8d0a096a3d497d8297b607e105
src/basic_memory/api/routers/utils.py,utils.py,source_code,5159,2025-07-03T18:43:10.079143,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),9e60f57da24e1dc9d64236c26a7a2353003dc618a77fbeb853c494aa3357a57c
src/basic_memory/api/template_loader.py,template_loader.py,source_code,8607,2025-06-21T00:01:59.576132,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),7b16d3797c89460c8b1664a335eae8c634fb5f60d6166d97e6a5156b776b6d83
src/basic_memory/cli/__init__.py,__init__.py,source_code,33,2025-06-21T00:01:59.456735,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),6ab70a2c05910e13c3ef1e6df34325be265e633c076741998a0cb2dcd295a069
src/basic_memory/cli/app.py,app.py,source_code,2268,2025-07-08T20:39:18.337708,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),f710670380d707c4458ad881f7a25bb3c5cdb2bcff59f139f37421264fe8f2f8
src/basic_memory/cli/commands/__init__.py,__init__.py,source_code,393,2025-07-08T20:39:18.338070,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),de8a23702f98fb844fa9f7fdbedc16ce24ff4f8baf05589c2f4a521cd8a55645
src/basic_memory/cli/commands/db.py,db.py,source_code,1480,2025-07-08T20:39:18.338326,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),704a1096d80ab9d12e247d029fe6223e935a0d0ceee7e615c020f46a720828e0
src/basic_memory/cli/commands/import_chatgpt.py,import_chatgpt.py,source_code,2856,2025-07-08T20:39:18.338787,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),8957cca3acab6351335624a518bdc19d956487e93d86dd1b6c2309e9d5850ae5
src/basic_memory/cli/commands/import_claude_conversations.py,import_claude_conversations.py,legal,2946,2025-07-08T20:39:18.339053,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7bc97838732bf00f4fb4a80e3ba4fdfbce8971ae85cc2ac902c3b3a3e110ae57
src/basic_memory/cli/commands/import_claude_projects.py,import_claude_projects.py,legal,2891,2025-07-08T20:39:18.339310,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),6321577075801c99ad47a0cdc13b5aa3c9ca102a05ca8f06ae2a51125a8c43bc
src/basic_memory/cli/commands/import_memory_json.py,import_memory_json.py,source_code,3013,2025-07-30T09:39:45.721637,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7ea193f6720b2672290ad16c79a143a31d88a1d567838f12b9ebf9110a7c0338
src/basic_memory/cli/commands/mcp.py,mcp.py,source_code,2593,2025-07-08T20:39:18.340025,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),3e1be3d27b18ef4743204b7cf8a37fbc2a1a91fc1636c468e08cefcd8597bac6
src/basic_memory/cli/commands/project.py,project.py,source_code,13836,2025-07-30T09:39:45.721838,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),af3e6f30410ab48355f4b1a46aaa3aa5423cb0a0490f51447db1d25e09f39916
src/basic_memory/cli/commands/status.py,status.py,source_code,5809,2025-07-08T20:39:18.340324,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),b6d0ea237d714d054f69b24a2058beba74e224a2fa4a80a525d62a2aa2ea2d70
src/basic_memory/cli/commands/sync.py,sync.py,source_code,8213,2025-07-08T20:39:18.340627,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),703b3e73c4c33850c27f6f4c07abbf5d4ce5dc43f706bf0c43264bbb45f8b213
src/basic_memory/cli/commands/tool.py,tool.py,source_code,9540,2025-07-01T08:33:22.070903,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),9b2fa400b9f7921bf55b602f8bbdfa36b1ec7e4a5bc8fe7b9838b92e31f0a9ed
src/basic_memory/cli/main.py,main.py,source_code,465,2025-07-08T20:39:18.340868,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7a405b12bff98e319968526238ae15859df0362bba5a3857562fecca47b5c70d
src/basic_memory/config.py,config.py,source_code,12487,2025-07-30T09:39:45.722031,jope-bm,5,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
src/basic_memory/db.py,db.py,source_code,7428,2025-07-08T20:39:18.341550,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),25e053b0b3f9e8fa823b05fc099001efe8a3a4f6e0f80728e5079ba45aa406d8
src/basic_memory/deps.py,deps.py,source_code,12144,2025-07-08T20:39:18.341870,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),bce28cd9ca549b8afce0bc689cc1fb2dc80cd5b929f85114aa682bb8ae82411d
src/basic_memory/file_utils.py,file_utils.py,source_code,6700,2025-06-21T00:01:59.587200,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),79ac5328b2c46d3232fcc6ff22ffc39ade085c0252ad91958be2a7ae9c9c8b71
src/basic_memory/importers/__init__.py,__init__.py,source_code,795,2025-06-21T00:01:59.295471,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),0537015bdecfded85cb166b9c3db50b2f3aef329c70e0c36fbcb4f824099a21f
src/basic_memory/importers/base.py,base.py,source_code,2531,2025-06-21T00:01:59.305335,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),6b0c1efd4f827f348834aa0cea2ae8eebbb842a2e5b1f5f3747ced0ef79b9f13
src/basic_memory/importers/chatgpt_importer.py,chatgpt_importer.py,source_code,7372,2025-06-21T00:01:59.308956,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),dfa540b196ab23b5c4e6bfcac4da567873351cf7c78fa35f4c3b87e84c58e218
src/basic_memory/importers/claude_conversations_importer.py,claude_conversations_importer.py,legal,5725,2025-06-21T00:01:59.288540,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),a7bc9e872f4f81ce5f79fe9dd1a6fd0f00a6f020a2cb2664184a97f14eeb1dbc
src/basic_memory/importers/claude_projects_importer.py,claude_projects_importer.py,legal,5389,2025-06-21T00:01:59.291737,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),a452675fd9bb18ebf64eb4bd7f69ccd7e993b617931015a3c4ab700d67493863
src/basic_memory/importers/memory_json_importer.py,memory_json_importer.py,source_code,4631,2025-07-30T09:39:45.722316,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),bc7d045299f17ed9ad5cebff7b1423250ec28a11130e47a0ac48d3ab82bdeafc
src/basic_memory/importers/utils.py,utils.py,source_code,1792,2025-06-21T00:01:59.298428,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),ee7e22fd4812362394f62ed8944abe80738c1e3ab88131faf639f4a8ccfd6c89
src/basic_memory/markdown/__init__.py,__init__.py,source_code,501,2025-06-21T00:01:59.316337,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),0ddce2a025ad0e729aab4e411d82e02ffefc15ac041cba4b5e7a7e90f4957c87
src/basic_memory/markdown/entity_parser.py,entity_parser.py,source_code,4516,2025-06-21T00:01:59.321461,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),1b0d1177358645ccbadd170b3f62760dc9bd838d38c741978ab0da5836234d68
src/basic_memory/markdown/markdown_processor.py,markdown_processor.py,source_code,4968,2025-06-21T00:01:59.325241,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),4b99f2ebdceed9d96a3bbb562682eba4b4b383c7a64080eaec3bbba25a4952c9
src/basic_memory/markdown/plugins.py,plugins.py,source_code,6639,2025-06-21T00:01:59.312733,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),82d2332918e866ccaf06a2e954d9eb9b397f71b4d9e591a7f2472e5f1423464a
src/basic_memory/markdown/schemas.py,schemas.py,source_code,1896,2025-06-21T00:01:59.329982,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),7b2c580abd615725a62267257b46ac13922dfc30fa01192a681658bb528ae67e
src/basic_memory/markdown/utils.py,utils.py,source_code,3410,2025-07-03T18:43:10.079889,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),1b757f0d06e60e3ea276c0b2ea44fe1a156a895e093c1e607d6286e702bf4ae4
src/basic_memory/mcp/__init__.py,__init__.py,source_code,35,2025-06-21T00:01:59.404757,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),76c0ce84aaa361e21b0942db1c8c5f708b536eab9d12e120d4da7ce9eab41844
src/basic_memory/mcp/async_client.py,async_client.py,source_code,925,2025-07-30T09:39:45.722547,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),6531f4387f30a30a322197ff51d6eacae4b25a58c648095931b3d1145d1da303
src/basic_memory/mcp/project_session.py,project_session.py,source_code,4203,2025-07-08T20:39:18.342906,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),4da403ec93dec9063eeb8b05246e3501740d162cc77a067d3ce54ba43d690819
src/basic_memory/mcp/prompts/__init__.py,__init__.py,source_code,615,2025-07-08T20:39:18.343263,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),f8197d0e08f64c3f4f50b8f38203ea5eebcf123582472ed2f58834de1dbe53b0
src/basic_memory/mcp/prompts/ai_assistant_guide.py,ai_assistant_guide.py,source_code,821,2025-06-21T00:13:53.262832,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),f13239c4e6e2455730bfac3d6f2d71407957d3086fc84efe2c6b22a833114c58
src/basic_memory/mcp/prompts/continue_conversation.py,continue_conversation.py,source_code,1998,2025-06-21T00:13:53.263309,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),aec9a50b657b7bb1ba0c02b42bcdb9bc5b0f2a0b1143bd361c5ce7ea59076833
src/basic_memory/mcp/prompts/recent_activity.py,recent_activity.py,source_code,3311,2025-07-01T08:33:22.073986,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),d2fd5cddbd927430f15d5b85f1e3a3368a18cb4e2e4587a5d29749d2b9e0830e
src/basic_memory/mcp/prompts/search.py,search.py,source_code,1692,2025-06-21T00:13:53.264093,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),9dbf3c319cbdb5d5bf32608b51522d8ae92b2dd6f7c441d62efd0954b525738a
src/basic_memory/mcp/prompts/utils.py,utils.py,source_code,5431,2025-06-21T00:01:59.425042,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),55a72b6eac18b724a9225608aca1e8e6cea3b684ccb1c609aab1511f7ce90ba4
src/basic_memory/mcp/resources/ai_assistant_guide.md,ai_assistant_guide.md,documentation,14777,2025-03-28T19:12:38.653627,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),aa76160e46256fe2662ae3a86799659916acfede1d5835c1fe2f0b136efb2e0b
src/basic_memory/mcp/resources/project_info.py,project_info.py,source_code,1906,2025-06-21T00:13:53.264918,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),2dc5244f1e225c17d4e8ba784d5721efc3aa2e8a5b3b2e258f22a77ebe155eca
src/basic_memory/mcp/server.py,server.py,source_code,1248,2025-07-08T20:39:18.343419,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),0a910cae27d61ea923e90fbe2d4b5c7cacf3b29d3e156f0b7f77872ab973c761
src/basic_memory/mcp/tools/__init__.py,__init__.py,source_code,1619,2025-07-03T18:43:10.080029,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co),872b771dd52ec3b763645a2baf6e10a70f049ce3106830a6d3f053b3e09a5fe6
src/basic_memory/mcp/tools/build_context.py,build_context.py,source_code,4611,2025-07-03T18:43:10.080426,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),724280b77b8f5f3e61cd3fdeebc3ee66e2bcfedaaea3638e6a2e2e33fcb1978e
src/basic_memory/mcp/tools/canvas.py,canvas.py,source_code,3738,2025-07-01T08:33:22.076204,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),db617d1bd81f3dbfa5f22d41e6b6b825afe1f73618f37acf63d9830390b98246
src/basic_memory/mcp/tools/delete_note.py,delete_note.py,source_code,7346,2025-07-01T08:33:22.076726,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),b52c9173f5600662f255e7a70a5c17d5293ef8b29c19a8403334d7da62b65c8b
src/basic_memory/mcp/tools/edit_note.py,edit_note.py,source_code,13570,2025-07-01T08:33:22.077181,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),ab8c7e7fbfa3fe5fb0ce6d7bf805454f5fd6182a340aae25237b1e6127b6d5a6
src/basic_memory/mcp/tools/list_directory.py,list_directory.py,source_code,5236,2025-06-21T00:13:53.267453,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),f85c43b02aeee580f4d8ce2a9100c0bab1095b245a0bb608e1847ace0d1ab51f
src/basic_memory/mcp/tools/move_note.py,move_note.py,source_code,17541,2025-07-30T09:39:45.722919,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),5ef27d9baaebc2c5656fc49c4798a4e4e40a70b6cd9d1e761dbcae021bad558c
src/basic_memory/mcp/tools/project_management.py,project_management.py,source_code,12944,2025-07-06T10:34:45.552403,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co),cdac736d65129f689a85c6b82f8e043bc84a31657ead7a8331771171eeaa860f
src/basic_memory/mcp/tools/read_content.py,read_content.py,source_code,9125,2025-07-30T09:39:45.723469,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),96a13addac507fd874394dfa2e1e5ed71af5eb89d0872c1d68cacf9cac3f8724
src/basic_memory/mcp/tools/read_note.py,read_note.py,source_code,8132,2025-07-30T09:39:45.723828,jope-bm,5,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),dff53ce02b16d9c5fca315b3f09bb70867a89d614b0ef3f0548dce87978048ee
src/basic_memory/mcp/tools/recent_activity.py,recent_activity.py,source_code,4833,2025-06-21T00:13:53.269120,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),5d58cd2402679b1bf3c7dfcbb3503e40e776c9347ba499524d3b91c528af98de
src/basic_memory/mcp/tools/search.py,search.py,source_code,15883,2025-07-03T18:43:10.081557,Drew Cain,4,Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),8519b0057468c4450eb543a2f561bcd0d7cb96e1ce1b95e948e02b309ba6b7ca
src/basic_memory/mcp/tools/sync_status.py,sync_status.py,source_code,10517,2025-07-08T20:39:18.344231,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),73dd73b186eaec8b82e6b17e39a67c27082d24e0653dc4e4b35712bd83b80a22
src/basic_memory/mcp/tools/utils.py,utils.py,source_code,20892,2025-07-30T09:39:36.616253,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),a95004911e276822eba88a3fef15c5b9ba861b1ca9a2ecfe0c1e3fb2f4ef0116
src/basic_memory/mcp/tools/view_note.py,view_note.py,source_code,2499,2025-07-01T08:33:22.080024,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),75d357c72113b1d0394987e521a4158ff09b77b23b08b56cddab5144331b1a68
src/basic_memory/mcp/tools/write_note.py,write_note.py,source_code,6612,2025-07-30T09:39:45.723985,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),771396fb49d397feb5f7e36321d4d233c22c4d1b53414912c849957c6e96fc83
src/basic_memory/models/__init__.py,__init__.py,source_code,333,2025-06-21T00:01:59.437212,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),8f40b876d162f85384690291f1d416106f9d26d750d793414e2260e276c85cd5
src/basic_memory/models/base.py,base.py,source_code,225,2025-06-21T00:01:59.447249,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),e2101727c084d519e32a16f6de53ddf9033b1bf15721d4e8c0c27d6d18b1b295
src/basic_memory/models/knowledge.py,knowledge.py,source_code,7087,2025-06-21T00:01:59.444376,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),005c5f292f1f45ae372aadc48c9080b9fa6d7b854d0bb7ecf587ec843ac1e28d
src/basic_memory/models/project.py,project.py,source_code,2773,2025-07-01T08:33:22.080907,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),a14ad06943aeeff9ae4a5fa2dfc0e1d07ce6085acc02dc20c402c3513b7d9397
src/basic_memory/models/search.py,search.py,source_code,1300,2025-06-21T00:01:59.440923,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),3e143cc38b5a0294af8e1d43a4f841e1c1fd193b76136a68f83159ce19e86646
src/basic_memory/repository/__init__.py,__init__.py,source_code,327,2025-06-21T00:01:59.256363,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),3162bea3c42292accea5ee52c8f6ca4368a8079056034529cfae6d820f84d035
src/basic_memory/repository/entity_repository.py,entity_repository.py,source_code,10405,2025-07-03T18:43:10.082570,Drew Cain,3,Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),e2a8d1eba6c8d64bc61d7168df0fe8c29a670858bf9daeac464a0f440273fae0
src/basic_memory/repository/observation_repository.py,observation_repository.py,source_code,2943,2025-06-21T00:01:59.269646,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),aa132f1cb4a36a84f715afdc40e2ac4f98d83e3eba1974b2b4404cc0b020ca04
src/basic_memory/repository/project_info_repository.py,project_info_repository.py,source_code,338,2025-06-21T00:01:59.252582,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),f172d50182a405643a19b2a3d62a80f4e2b41463c775394eb3b66d7de51ff6df
src/basic_memory/repository/project_repository.py,project_repository.py,source_code,3803,2025-07-30T09:39:45.724344,jope-bm,2,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),9083ba214b2bcaaf98164d9c33cda7add52bc77f7269ecb6e43cdd5088728b7c
src/basic_memory/repository/relation_repository.py,relation_repository.py,source_code,3179,2025-06-21T00:01:59.246745,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),cfb3a8e59cff27e063e91bd00e949647bdd92e4d9fc46edeee3acc6de15e26f4
src/basic_memory/repository/repository.py,repository.py,source_code,15224,2025-06-21T00:01:59.273355,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),3096fe71bf106506cbf86aeafe2aafe0aabbe5a5f6c90a212c8aa1e53e1f171c
src/basic_memory/repository/search_repository.py,search_repository.py,source_code,21936,2025-07-03T18:43:10.082844,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),a972f73d1b71dac5770e8eb3793c6c9ac44e4e792f9dab528f8c4e6c6a80bcaa
src/basic_memory/schemas/__init__.py,__init__.py,source_code,1674,2025-06-21T00:01:59.514555,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),98480815c75379bfafe32d2090e87fa40e73caa19b664fbd5db5ea9528ba58cb
src/basic_memory/schemas/base.py,base.py,source_code,6738,2025-07-01T08:33:22.082193,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),171f7b0c4ab33abef2f7379eb1e3bda950586ce7edff83907fd12255f84e267e
src/basic_memory/schemas/delete.py,delete.py,source_code,1180,2025-06-21T00:01:59.502563,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),50047624af7d58c8f780ffb2a065a51c3dde64492f9534917fc42860813e59fc
src/basic_memory/schemas/directory.py,directory.py,source_code,881,2025-06-21T00:01:59.529023,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),17dfcbac9a91a9bfe43b4f060ca2735cb6f69e16d81b63dd554a397830ff29fe
src/basic_memory/schemas/importer.py,importer.py,source_code,693,2025-07-30T09:39:45.724633,jope-bm,2,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ac33df423ca32b28ce7b6ea9c29d541f8783a86c0c29f78db35163bf93f139cd
src/basic_memory/schemas/memory.py,memory.py,source_code,5833,2025-07-03T18:43:10.083167,Drew Cain,3,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),acb4a953a553feca672c4895798a7d948ec51f922f75b66dd7d55716e3bebed3
src/basic_memory/schemas/project_info.py,project_info.py,source_code,7047,2025-07-01T08:33:22.083248,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),7dc3635297b6e7fe6e3262b2d78da26f7a79a846a4cecd5624f2e4828979cf2c
src/basic_memory/schemas/prompt.py,prompt.py,source_code,3648,2025-06-21T00:01:59.525753,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),4a92157d9a6b413f04e6e3f8d23dc2a417369c729f9703a8de2643ec114f2071
src/basic_memory/schemas/request.py,request.py,source_code,3760,2025-06-21T00:01:59.511789,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),32fe44beb2d99452223ebf1d3a3a3fe105ef92c79885023b71dfd7db30ecc503
src/basic_memory/schemas/response.py,response.py,source_code,6450,2025-07-03T18:43:10.083478,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),5eea4660a2abe48d83ed083d1c2483fdcfb403e0b504f03c14d22f1cae866abb
src/basic_memory/schemas/search.py,search.py,source_code,3657,2025-06-21T00:01:59.522804,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),cb032c0c60102b6b0ed934f995cf9d6bf93aece296d71d537a75e8ae61c75afe
src/basic_memory/services/__init__.py,__init__.py,source_code,259,2025-06-21T00:01:59.655059,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),5c6b7c597ddf5ffd0af4bdfb32ccbc1c5f2794c658206dee43a9945fafe226d8
src/basic_memory/services/context_service.py,context_service.py,source_code,14462,2025-06-21T00:01:59.672086,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (paulmh@gmail.com),e1178b005e6a89f03d6b239e3c6b152aeb30d535a3f280734440a3aec16228f2
src/basic_memory/services/directory_service.py,directory_service.py,source_code,6017,2025-06-21T00:01:59.648501,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),fd838f5ec79033892777b3c8140868f4b57f13e1658d513959524a434b9f959b
src/basic_memory/services/entity_service.py,entity_service.py,source_code,30440,2025-07-03T18:43:10.083956,Paul Hernandez,4,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),7cd5163eca6b8a0772e838c8c869e478166763cd6a2d745b0b9aa5c25a65baee
src/basic_memory/services/exceptions.py,exceptions.py,source_code,390,2025-06-21T00:01:59.668908,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),a158d0af9d1742a9c5ab5f8c34a062948d9286d1c3c72a5abf20e4d547b21e1c
src/basic_memory/services/file_service.py,file_service.py,source_code,10059,2025-06-21T00:01:59.641785,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),8c2ae69c4913438b7d1c5ecbfcce812fbb5d0ea8e3cedcbd8694e8f40cf086f3
src/basic_memory/services/initialization.py,initialization.py,source_code,6715,2025-07-08T20:39:18.344482,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),4dde58b799cf4ac90631e59949b55b3355a93bdf99df0d9c2408c0cea67e10c4
src/basic_memory/services/link_resolver.py,link_resolver.py,source_code,4594,2025-07-01T08:33:22.084908,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),d7efd516cbea753e6b5411def09aeaeb7539f5743487119ecf163c736e138a8c
src/basic_memory/services/project_service.py,project_service.py,source_code,30125,2025-07-30T09:39:45.725079,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),bce95c00aa9a1da0a8c8090ee34b5b1494207f8c99e6c25de4ec43fddc38f6be
src/basic_memory/services/search_service.py,search_service.py,source_code,12782,2025-06-21T00:01:59.665439,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),7392b2d2e7f3ed83c5807855ccd450e0e79c17f254aedee700bce9323a1b5b83
src/basic_memory/services/service.py,service.py,source_code,336,2025-06-21T00:01:59.636167,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),57e77ff20395d3bcc62100e92fe2acaacdd937d977a9fdc764e2b57ff60d4da8
src/basic_memory/services/sync_status_service.py,sync_status_service.py,source_code,7504,2025-07-03T18:43:10.084229,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),0a025d689e8e16f1632872104952105fc904537f3d32bcff592eb1f1dc76fbb7
src/basic_memory/sync/__init__.py,__init__.py,source_code,156,2025-06-21T00:01:59.621137,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),0951e0b981f8e7b876bb6c6833c2af3a29490bbd5726567ea9487c947723623e
src/basic_memory/sync/background_sync.py,background_sync.py,source_code,726,2025-07-08T20:39:18.345062,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),549af64ae91128b76c6df07ef517b82de85ca4ad796be44b5c0141fac01d4513
src/basic_memory/sync/sync_service.py,sync_service.py,source_code,23387,2025-07-01T08:33:22.086507,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),0310b927561370f593980d07773bce641b618b8fbf2d9e3890d174290a0344fc
src/basic_memory/sync/watch_service.py,watch_service.py,source_code,15316,2025-06-26T09:28:51.859209,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),240ba6ac7523575945f4db442b7da3820d26c9605f2d7a2d357c4e35e215f523
src/basic_memory/templates/prompts/continue_conversation.hbs,continue_conversation.hbs,templates,3076,2025-07-01T08:33:22.086792,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),b6bac31d25c0e52d0909b2273285092f4e31bc219107f92ed511cd407b65e992
src/basic_memory/templates/prompts/search.hbs,search.hbs,templates,3098,2025-05-25T10:07:54.520119,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),1f570222c1caa78542d46ac7d8a79407ca467b9bd714fa9bd953e8b72a667820
src/basic_memory/utils.py,utils.py,source_code,8654,2025-07-30T09:39:45.725428,jope-bm,5,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); andyxinweiminicloud (andyxinweimin263@icloud.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
test-int/conftest.py,conftest.py,source_code,8203,2025-07-08T20:39:18.345735,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),7231e1bc76cddc7c94433e853825dc0165b103bedf996c2c24eda6ae32fff5ad
test-int/mcp/test_build_context_validation.py,test_build_context_validation.py,source_code,6469,2025-07-08T20:39:18.346021,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),ee61d6b15cee0194fd74c1bcf9361d5a839f6081c7d571ef081da8fcefe1bbf2
test-int/mcp/test_delete_note_integration.py,test_delete_note_integration.py,source_code,13175,2025-07-08T20:39:18.346366,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),497495249b8bcf0af177d8705c6899875460707697980434e560cfe56f86feb3
test-int/mcp/test_edit_note_integration.py,test_edit_note_integration.py,source_code,19822,2025-07-08T20:39:18.346730,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),9ba704badd05808aee0b5a686b7e250e1c865229ec92dab561cba5287b6f6fc6
test-int/mcp/test_list_directory_integration.py,test_list_directory_integration.py,source_code,14744,2025-07-08T20:39:18.347106,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),2c4336a59de63f17ed252bb52d1330ebca901ad83bfebc9e5aa67f132a3c77c6
test-int/mcp/test_move_note_integration.py,test_move_note_integration.py,source_code,19825,2025-07-08T20:39:18.347344,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),cbb17688d0f9551306a7520b059ace7f7dfba70fc20bf0ec98b1148000d7ad7d
test-int/mcp/test_project_management_integration.py,test_project_management_integration.py,source_code,33175,2025-07-08T20:39:18.347658,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),93a43cd699453a7602a5def839740ba5027ab914eba3e7b66a026860ed6bd567
test-int/mcp/test_project_state_sync_integration.py,test_project_state_sync_integration.py,source_code,7728,2025-07-08T20:39:18.347907,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),80b0f5b5cd359498dcbd3af7dcb6194afa4582d8efb3faa38d28675ee45e0748
test-int/mcp/test_read_content_integration.py,test_read_content_integration.py,source_code,11187,2025-07-08T20:39:18.348233,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),6955dd343b40df088ac79175e52723501d83796335a16e43b7f58e16e757e1b8
test-int/mcp/test_read_note_integration.py,test_read_note_integration.py,source_code,1381,2025-07-08T20:39:18.348463,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),b93050da68d0e9d47d46879a386567f688399e28390f4eb31ee37353dc27514b
test-int/mcp/test_search_integration.py,test_search_integration.py,source_code,14690,2025-07-08T20:39:18.348959,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),e0957ada9dd9b12eae5843a37d241ec9293ca929f18281e31197dae36bff589d
test-int/mcp/test_write_note_integration.py,test_write_note_integration.py,source_code,9472,2025-07-08T20:39:18.349398,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),b2b56fee8351168d0c65640c295d969dc4575d9bea87927b2dc07e591cabdff3
tests/Non-MarkdownFileSupport.pdf,Non-MarkdownFileSupport.pdf,other,106751,2025-02-23T17:31:23.206428,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),4aa96f9d0ee830ef6a1ec92243f302bbe2fa8971b84945f4eb5e276bf1ffde96
tests/Screenshot.png,Screenshot.png,other,190076,2025-02-23T17:31:23.207771,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),dd82612fe013c39810d28ae0f2bbcca6ab2c9925b035d7720bb02493ddf5d0a9
tests/__init__.py,__init__.py,source_code,141,2025-06-21T00:01:46.216456,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),2ac5605e69ad8829e96b3689632536539f1cdd3149a29c1903f25af0793a5d65
tests/api/conftest.py,conftest.py,source_code,1404,2025-06-21T00:01:46.359728,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),ea46cde47a37c1f43fe079e48f5d0de8007155a0187f4445abc4f2ffde9c1791
tests/api/test_async_client.py,test_async_client.py,source_code,1382,2025-07-30T09:39:45.725666,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),a2f96226a75e5cff2326ca48ddccea4915964d83e529f7b99958bc233ccdc31a
tests/api/test_continue_conversation_template.py,test_continue_conversation_template.py,source_code,5152,2025-06-21T00:01:46.390901,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),cca08b0291db0ad693c60d8dc203660021feab46d9eb530be7f67e0ebb52c1d3
tests/api/test_directory_router.py,test_directory_router.py,source_code,9376,2025-06-21T00:01:46.356659,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),428626589f0010512481ba495368b0bf2b9617b22764e588eaa3562e86da4d37
tests/api/test_importer_router.py,test_importer_router.py,source_code,16578,2025-06-21T00:01:46.368070,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),03edf6e7404c7350afe76e54ea2bf0d66f668bcdb49f59702067a67b3694309d
tests/api/test_knowledge_router.py,test_knowledge_router.py,source_code,45771,2025-07-01T08:33:22.090490,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),43a5f91ae1fb9038e65c09701336fbdc4dbf40c8319c94727e6312fda9d6d56e
tests/api/test_management_router.py,test_management_router.py,source_code,6220,2025-06-21T00:01:46.402764,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),f6a3f41f263f10c08281525571e2e39fc9ae818ebb7e9895e3f3f3115ea05677
tests/api/test_memory_router.py,test_memory_router.py,source_code,5555,2025-06-21T00:01:46.399667,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),2b8782015c893081fc67b7f13a7255d4e3dc45b27705a524f94918a339e2d486
tests/api/test_project_router.py,test_project_router.py,source_code,13288,2025-07-30T09:39:45.725847,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),af85d0870069d3831a9214372853994e5d12dc910e37298eb9958f72f6a8bb31
tests/api/test_project_router_operations.py,test_project_router_operations.py,source_code,1812,2025-06-21T00:01:46.381705,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),afd40879de3d7c508cca0feeae3cb7a8f3afcfbb06f59a6104189b1e3291376a
tests/api/test_prompt_router.py,test_prompt_router.py,source_code,5046,2025-06-21T00:01:46.396588,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),9115995b7dd8b44d8a9c06c5e1bbb177618286675a769889e35ac16f26fd9897
tests/api/test_resource_router.py,test_resource_router.py,source_code,13960,2025-06-21T00:01:46.384919,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ad3cb2cabaeab82a7e64253ceede7c1791e2bd70c92f60bc5f3be939292ca22d
tests/api/test_search_router.py,test_search_router.py,source_code,6409,2025-06-21T00:01:46.375427,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (phernandez@basicmachines.co),ba3e3809795e72db9dd9c8c729048bdbbbdb897c2a5aa8b175112b5d4b56d152
tests/api/test_search_template.py,test_search_template.py,source_code,5258,2025-06-21T00:01:46.378721,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),8f0d16a1de947d0466d96afed02e695f7648adac70b9ba99753250068207a686
tests/api/test_template_loader.py,test_template_loader.py,source_code,8035,2025-06-21T00:01:46.406694,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),4fd7c6c4862cc0d877d29244ecdc0b52a0693fc4574a9667663bee36bfd36b62
tests/api/test_template_loader_helpers.py,test_template_loader_helpers.py,source_code,7445,2025-06-21T00:01:46.372114,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),8dd35f11c9b3f2d96ac36263edf4430308728c04c9da543b1cb845b96ab20aa7
tests/cli/conftest.py,conftest.py,source_code,1185,2025-07-01T08:33:22.090848,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c3c73da7ad73224931e1fba4ebd70792221be607bfb1488bb5c9420bba03bdb8
tests/cli/test_cli_tools.py,test_cli_tools.py,source_code,13528,2025-06-21T00:01:46.318964,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),befb8cab50132e26a1b13b82ce8e93a3258282cf157a4230214bfcefeae65102
tests/cli/test_import_chatgpt.py,test_import_chatgpt.py,source_code,6719,2025-07-08T20:39:18.350234,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),df447335545248e2fb2417af33f8cc2f0143bd413a1783e41f89605de305b7c8
tests/cli/test_import_claude_conversations.py,test_import_claude_conversations.py,legal,5028,2025-07-08T20:39:18.350607,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ac4b3b9dfc331fa7fa32a2941c027ff8b6585a94a5ccfaaf0a4affe5cf19c00c
tests/cli/test_import_claude_projects.py,test_import_claude_projects.py,legal,4585,2025-07-08T20:39:18.350885,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7eaab3d491d50ca59c8ba015ecd5740a5feae94d970ee17559c6df9d8558c2cc
tests/cli/test_import_memory_json.py,test_import_memory_json.py,source_code,4891,2025-07-30T09:39:45.726290,jope-bm,3,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),d6734ce059dacf97bc764d848b2e2a6ba52fa96871bc331ec9d2ddc60b766bb6
tests/cli/test_project_commands.py,test_project_commands.py,source_code,6578,2025-07-30T09:39:45.726703,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),51e2ac7ff44b0d1a1db6da406c5c2e6aebceb1b63a1e8d5ab51bbaa834507157
tests/cli/test_project_info.py,test_project_info.py,source_code,3874,2025-07-01T08:33:22.091600,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),0b08950e95a5f276f14c56f6fe99af4d41b8f04548e9b7746f807c5ac4155578
tests/cli/test_status.py,test_status.py,source_code,4160,2025-07-01T08:33:22.091978,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),a1ff89fdac4bf57ba03be92ac79b8ca3210af0b1132d5a2bd0fc0c253fcbf988
tests/cli/test_sync.py,test_sync.py,source_code,3864,2025-07-08T20:43:47.816788,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),77e0f6b42b584a5acfda3c2ff194ff5e71ca57a40cb4484d563f5482bbbb4c0f
tests/cli/test_version.py,test_version.py,source_code,289,2025-06-21T00:01:46.305368,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),db95665178a32d391bd83011d073db269cefba9da8a4dc4d3e392d47bc0e0a8f
tests/conftest.py,conftest.py,source_code,15286,2025-07-08T20:39:18.351368,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),c6f38f103ce58144ffe4562f0c47dad849974e96327f02805e854447e19bc411
tests/importers/test_importer_base.py,test_importer_base.py,source_code,4329,2025-07-08T20:39:00.549594,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),da872a073f2b90fda3c98c9f44cf45884f4f682d91ec1b1f931d42c34eee1939
tests/importers/test_importer_utils.py,test_importer_utils.py,source_code,1827,2025-06-21T00:01:46.184343,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),cbee630c855ceb1de764ac0cdb1d5d781c16cd3a934d67166adc235c98f363d7
tests/markdown/__init__.py,__init__.py,source_code,0,2025-06-21T00:01:46.196570,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
tests/markdown/test_entity_parser.py,test_entity_parser.py,source_code,8669,2025-06-21T00:01:46.206579,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),496fdab6fe164634f8223f827585a4c0dedd30ccea7b0b69dc79004ad0996de2
tests/markdown/test_markdown_plugins.py,test_markdown_plugins.py,source_code,5139,2025-06-21T00:01:46.200759,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),9624d73462aadcd47fb0edd0d87880959a8982eecfa39529f21e3b292c34cb83
tests/markdown/test_markdown_processor.py,test_markdown_processor.py,source_code,5728,2025-06-21T00:01:46.194175,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),35042aa57dd2aeed848c624aa7f69decfb18e83c3b217eca1fdacfc4a10831db
tests/markdown/test_observation_edge_cases.py,test_observation_edge_cases.py,source_code,4295,2025-06-21T00:01:46.209598,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),6f1ab7684bfa8b02af0e0c42b3d7aed91fcd0b1c6288cbc9b6d6943c166979fa
tests/markdown/test_parser_edge_cases.py,test_parser_edge_cases.py,source_code,5142,2025-06-21T00:01:46.213291,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),d511d7d1187e3c1d3a8d3e0ab9efc9e68e84115c46811e7a9dc21eca9a87d520
tests/markdown/test_relation_edge_cases.py,test_relation_edge_cases.py,source_code,3952,2025-06-21T00:01:46.203846,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),f8e2016308f84139342cc37ec6e389ffe52d7eb46f998ee327c7f0d5bba3ad9b
tests/markdown/test_task_detection.py,test_task_detection.py,source_code,401,2025-06-21T00:01:46.191444,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),37fe91a689742033a0eb76723557e31c2a85e591133383b3887572cb714db2a5
tests/mcp/conftest.py,conftest.py,source_code,1761,2025-07-08T20:39:18.351554,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),350b2ca42c52ef76e002deddb65fc0eccb9f8989bd26078ea1c100e3b93bae3f
tests/mcp/test_prompts.py,test_prompts.py,source_code,5989,2025-07-01T08:33:22.093691,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),e3b2ee5b4bc120b5043225ebdbf73eadd71be9c3d9d8e1fdd66a24a8ff11a780
tests/mcp/test_resource_project_info.py,test_resource_project_info.py,source_code,4885,2025-07-01T08:33:22.094085,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),6a043736789b239c8d4ae00d1a5332a0fc3bed4038fe3388970ba97dc7431a93
tests/mcp/test_resources.py,test_resources.py,source_code,549,2025-07-01T08:33:22.094481,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),77a6396b9b8a9d3b3d202e438243ca6178d87fd7680c9fc063ee013f2042aa4c
tests/mcp/test_tool_build_context.py,test_tool_build_context.py,source_code,3714,2025-07-01T08:33:22.094661,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),5f0a39fdb652605335ed54aa91fe994262441bb7ba46f381c46ebca882fc23f2
tests/mcp/test_tool_canvas.py,test_tool_canvas.py,source_code,7997,2025-07-01T08:33:22.094982,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c517bb1f9edc77b76fbeb483dfcb2dd57de9f0b80a10738708bc90cbe388dc9e
tests/mcp/test_tool_delete_note.py,test_tool_delete_note.py,source_code,4208,2025-07-01T08:33:22.095578,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),4cbf3ba4313438651b0608d5b25faf1400f7bd6a78af630351aecd4457800895
tests/mcp/test_tool_edit_note.py,test_tool_edit_note.py,source_code,13503,2025-07-03T18:43:10.084890,Drew Cain,2,Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),dd441969aa54b3739f2d9202d3feaf46aee807ec51e01a13ef41eda52743944f
tests/mcp/test_tool_list_directory.py,test_tool_list_directory.py,source_code,7730,2025-07-01T08:33:22.096737,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),cfbb5c06bcfe74ea60f31051eb9b03ac80b86232b053c188bf3b172132f7c0f5
tests/mcp/test_tool_move_note.py,test_tool_move_note.py,source_code,25851,2025-07-30T09:39:45.727347,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co),c9f95158503fa42e37532aaa35027538fa49cea718d059d570cf547d31f01043
tests/mcp/test_tool_read_content.py,test_tool_read_content.py,source_code,19442,2025-07-30T09:39:45.727526,jope-bm,1,jope-bm (joe@basicmemory.com),1817e32f997b06dbdabf02864d0c70584c1cd80bf1fae8dfbef649de79434bc5
tests/mcp/test_tool_read_note.py,test_tool_read_note.py,source_code,22991,2025-07-30T09:39:45.727746,jope-bm,4,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),4e13ea9ff977525aa62b6a8c22505ffe989329dc3e7d07b71e044cf43ef3ddfd
tests/mcp/test_tool_recent_activity.py,test_tool_recent_activity.py,source_code,3983,2025-07-01T08:33:22.098561,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),45414152cd45334b84df56f653146b7bd2939907548e7245a37207fdccfc4c87
tests/mcp/test_tool_resource.py,test_tool_resource.py,source_code,6782,2025-07-01T08:33:22.098948,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),3916ae1c4d458eee53d2c570b9bbb708ecd617de5a145c3a7552389954bc9195
tests/mcp/test_tool_search.py,test_tool_search.py,source_code,10959,2025-07-03T18:43:10.085177,Drew Cain,3,Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),07839cf8e89d74ba05ec1dc55fa842444c1f0cc3d1587823b0ac3458ad89e87c
tests/mcp/test_tool_sync_status.py,test_tool_sync_status.py,source_code,6331,2025-07-01T08:33:22.100021,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),3f3566df5b9f3c66bb9b3bd77d4c05825480d0e85015305cb1b7ab9429c7a7ec
tests/mcp/test_tool_utils.py,test_tool_utils.py,source_code,9228,2025-07-01T08:33:22.100561,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),b50789f7a78b0169f52dc8bd16e262510ed4a0ac130fce698d4be84af1fe2d31
tests/mcp/test_tool_view_note.py,test_tool_view_note.py,source_code,10004,2025-07-01T08:33:22.101164,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),385535ce791c3ab55db827cbcf27ea25ae85faf3a027f52df4eea052cede7f36
tests/mcp/test_tool_write_note.py,test_tool_write_note.py,source_code,35136,2025-07-30T09:39:45.728137,jope-bm,3,jope-bm (joe@basicmemory.com); Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
tests/repository/test_entity_repository.py,test_entity_repository.py,source_code,15498,2025-06-21T00:01:46.176265,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),8f5d1ff6cff7b640993ff73f463b1e7801a4dd69640fc55fa23dc58b865e9943
tests/repository/test_entity_repository_upsert.py,test_entity_repository_upsert.py,source_code,16983,2025-07-03T18:43:10.085482,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com),ee0ecd6f4ff8b31838bd6de6fb1018af437e02813d8315c06f4e20a6b78a0572
tests/repository/test_observation_repository.py,test_observation_repository.py,source_code,11786,2025-06-21T00:01:46.158862,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),3006d392ad94dcc9b3ae031d63a1af6935624534389dd025de70d4c9e112aa0b
tests/repository/test_project_info_repository.py,test_project_info_repository.py,source_code,1186,2025-06-21T00:01:46.161876,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),dbce7ae7350403cbc37343c2e3e68e4d2255e247006aaa6c495a7ce8bfddea7d
tests/repository/test_project_repository.py,test_project_repository.py,source_code,10511,2025-07-30T09:39:45.728536,jope-bm,2,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),279a3324635b1b515c18003bd1875cf230fef1192cadef9ca426846783717e75
tests/repository/test_relation_repository.py,test_relation_repository.py,source_code,11980,2025-06-21T00:01:46.167567,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),fcdbdf40f7b56145b98eedde5ead64ef6187e859adde48cc09f5a97754757428
tests/repository/test_repository.py,test_repository.py,source_code,5996,2025-06-21T00:01:46.152691,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),3a6d4ca0b7a438a30ae504f5eac22874d4e1a937a4246e90efad46c90f0e1c95
tests/repository/test_search_repository.py,test_search_repository.py,source_code,25471,2025-07-03T18:43:10.086041,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),805b5cc55a53ae9d6e86c0de735c7f38ac9fd92c23194b0150fa3e7f5507a146
tests/schemas/test_memory_url.py,test_memory_url.py,source_code,2000,2025-06-21T00:01:46.346515,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),946fa6c48534f41c8b9cc27bbad164d3e3673a79afa84fbb439fae4cd26c504b
tests/schemas/test_memory_url_validation.py,test_memory_url_validation.py,source_code,9338,2025-07-01T08:33:22.103247,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),7054c3a3cdb8e05d2ffd254ed298908ff3d48a0a3927d960975eae1ab9beeb38
tests/schemas/test_schemas.py,test_schemas.py,source_code,16880,2025-07-03T18:43:10.086337,Drew Cain,3,Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),2c746a00c340f063c160ace67569955a300a294040949695c79fd4ae124fb611
tests/schemas/test_search.py,test_search.py,source_code,3264,2025-06-21T00:01:46.343685,github-actions[bot] (AI Assistant),2,github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),7437775d1b4a61a438cbabe3dbb1becd2adc4dbcc0c24595830c0843fa7a3166
tests/services/test_context_service.py,test_context_service.py,source_code,8833,2025-06-21T00:01:46.446200,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),ab49d21a05d5364e7e8a2f00b07aabe1109b7417698e0acd92792e2cb5f22732
tests/services/test_directory_service.py,test_directory_service.py,source_code,6854,2025-06-21T00:01:46.457258,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),a0c765fb6bd1a75b6ba4592b1df344c48273b6ab03ba6bd15d2b5e840eb2d521
tests/services/test_entity_service.py,test_entity_service.py,source_code,59278,2025-07-03T18:43:10.086674,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),d7d495756faa10b415d705cf26b69d2efbc5f190a834b6767386ff296629ea71
tests/services/test_file_service.py,test_file_service.py,source_code,5243,2025-06-21T00:01:46.460141,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),83e6e9a7584b7baeccbc84865d8c1d3e2a0223e7d2b9c066d3799c289196ab7d
tests/services/test_initialization.py,test_initialization.py,source_code,6939,2025-07-08T20:39:18.351946,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ebb8d11ff37dcb36c4844c68f873c8e0e8aa6ab1b82abb22ee96c4233e6ab287
tests/services/test_link_resolver.py,test_link_resolver.py,source_code,13618,2025-07-01T08:33:22.105766,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),c9fc56d0c3572770e95ca6175c213ed8b44c2a6ddddc298b5662cc2d8f41014e
tests/services/test_project_service.py,test_project_service.py,source_code,28152,2025-07-30T09:39:45.728920,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),430868ed796518937a3b0bed4d8c33be53264a6100d61d54b0745bb2b58bf29a
tests/services/test_project_service_operations.py,test_project_service_operations.py,source_code,4679,2025-06-21T00:01:46.442141,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),d4103b6c5fff7e74ba84b86033537b8522aa60fc75aec4974d52e1dfec68b1b4
tests/services/test_search_service.py,test_search_service.py,source_code,25299,2025-06-21T00:01:46.439133,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),b3e58258db2b727ab8a1792e52fcb038d3a0779bcd1b70f2d295a9d06e5e2513
tests/services/test_sync_status_service.py,test_sync_status_service.py,source_code,9926,2025-07-03T18:43:10.087266,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),00245ca356e33fc7afcd2d42cb5ec082038c95383d738067821cf1e8d23c9a4b
tests/sync/test_sync_service.py,test_sync_service.py,source_code,39659,2025-07-01T08:33:22.107088,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),0609aee9906549f27fc4289b5ac2d46a50e53e12a0615a2701f17fbe9a74e169
tests/sync/test_sync_wikilink_issue.py,test_sync_wikilink_issue.py,source_code,1959,2025-06-21T00:01:46.420370,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),db4b98dd7008a1a26b0fd02b9a2d03bca950b281efc19d98a91b017c500b23a5
tests/sync/test_tmp_files.py,test_tmp_files.py,source_code,5930,2025-06-21T00:01:46.424277,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),cead95a07b29f6c1ab933abe67f9648122f5f33d33753c8e4ab4934af96cbc25
tests/sync/test_watch_service.py,test_watch_service.py,source_code,14076,2025-06-26T09:28:51.860679,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),e324a4127f8bfe59798f3a08125488c701e88239b7482d87a0394b91c55b893e
tests/sync/test_watch_service_edge_cases.py,test_watch_service_edge_cases.py,source_code,2169,2025-06-21T00:01:46.409830,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),b87c5015330e9b551630aab7ef95ac1e7663f0ce46e912d8155b319e139559aa
tests/test_config.py,test_config.py,source_code,3680,2025-07-03T18:43:10.087558,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),6a246431cff739274efda895dfa28f38710289a73e645312f5fa05fce0885ec8
tests/test_db_migration_deduplication.py,test_db_migration_deduplication.py,source_code,6166,2025-07-08T20:39:18.352690,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com),ab768e061531ca2edcd598c101b5d884e549f90c7a4b305a23713a199d263fbb
tests/utils/test_file_utils.py,test_file_utils.py,source_code,6199,2025-06-21T00:01:46.219673,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),4615bb20b92b8a79d9e086301f852b34c3360f17ecc209920d68528268ddeed4
tests/utils/test_parse_tags.py,test_parse_tags.py,source_code,1691,2025-06-21T00:01:46.223400,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),b83afdf18dac8b3411bd0fab1301683217d3d1abceaf2d22c09e79c709efd8eb
tests/utils/test_permalink_formatting.py,test_permalink_formatting.py,source_code,4193,2025-06-21T00:01:46.225738,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),d998a4ca846c78679728a918b6f71df67c30b6961ede3cffa609f2cf6ad160eb
tests/utils/test_utf8_handling.py,test_utf8_handling.py,source_code,5791,2025-06-21T00:01:46.228483,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),50ec792e572af51cd2d996d4ddacbcfd3bbd79aa928818a90c42117451ea829f
tests/utils/test_validate_project_path.py,test_validate_project_path.py,source_code,15346,2025-07-30T09:39:45.729224,jope-bm,1,jope-bm (joe@basicmemory.com),27a4bf2a9d277e6cd773619e5241b20175d211ec60c284361bec404fd99143f3
uv.lock,uv.lock,other,232710,2025-07-30T09:39:36.619155,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),caa1e3f6e66464b884a78cab77df2fe303fa3c0dc573be0196474b6d3c150581
1 path name category size_bytes modified_time primary_author contributor_count contributors_list sha256_hash
2 .claude/commands/release/beta.md beta.md documentation 3103 2025-07-01T08:33:22.037438 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 345f06e3f304470a8892e96979355919c8e5d8f4212ebbe543c3272fd15a5cbe
3 .claude/commands/release/changelog.md changelog.md documentation 4648 2025-07-03T18:43:10.075727 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) ea2484352f4f881aa71f546dcf84b0882d60c96673a88406aa2f7e12a282a1f4
4 .claude/commands/release/release-check.md release-check.md documentation 3344 2025-07-01T08:33:22.040060 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 7dd41a40e95c89153936c4ea60a3c989eccd5973fff9f6caef156de82f12bd04
5 .claude/commands/release/release.md release.md documentation 2925 2025-07-01T08:33:22.043674 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 0e4afd788fcf85fbe46094452e9b92a4290c3e23702283e72ee56aa9288476e5
6 .claude/commands/test-live.md test-live.md documentation 18720 2025-07-03T18:43:10.076403 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 21a9edeefb3e5673d833151a2fd0464087c0f0a9d056fdd7fca474c1d177d6fc
7 .dockerignore .dockerignore other 623 2025-07-01T08:33:22.048301 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) b3079bffad89d07186c9166725075524ffc07ceea6dc1d53fdbea6dca9585684
8 .github/ISSUE_TEMPLATE/bug_report.md bug_report.md documentation 967 2025-03-01T14:22:50.968994 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 0d34443ddda84a5e9726c8268083c9646d2cc64ff9c5ebf56c89587cef4c7706
9 .github/ISSUE_TEMPLATE/config.yml config.yml configuration 374 2025-03-01T14:23:26.936836 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) f4e21bb9b7f053509b6c08548a2649a5d62f44f0106810aa8c915d2f29acc623
10 .github/ISSUE_TEMPLATE/documentation.md documentation.md documentation 557 2025-03-01T14:23:40.020117 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 78f80a6b9a2b954695f74a847a068e0b780da6020572401c13c931555a4a9db5
11 .github/ISSUE_TEMPLATE/feature_request.md feature_request.md documentation 786 2025-03-01T14:23:06.344081 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) cee834de442221d53f3d2eeb85b81f3a641b3a7cb587ef8a169452305007439d
12 .github/dependabot.yml dependabot.yml configuration 529 2025-03-15T10:40:16.936258 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 8c6c2ef9a1417da63adb49c70f45524129bd5c46bbd7452b6b97cfe65205f89d
13 .github/workflows/claude.yml claude.yml legal 4628 2025-07-03T18:43:10.076958 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) d026f1756310781b85abc491b454cc31b52dfce4b951cdf8a4a555b6b047604c
14 .github/workflows/dev-release.yml dev-release.yml configuration 1405 2025-06-03T08:31:42.915367 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 58bcfc9e11e210e02dee76aabe7d1378da4edafb7f61857049ee6ccee9d45ffa
15 .github/workflows/docker.yml docker.yml configuration 1621 2025-07-01T08:33:22.049870 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c918a86f2b16ebb45182a73c45272bcd6cea7022cc6e0eaeb2b2a24d8eebfe4d
16 .github/workflows/pr-title.yml pr-title.yml configuration 887 2025-02-17T16:29:35.733646 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) b6df62a20235496feba4d9419c85d6e8446c4d447ad69e7f57b51cc8953f68f4
17 .github/workflows/release.yml release.yml configuration 2479 2025-07-03T18:43:10.077261 Drew Cain 2 Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co) 163307a99e2f3d317af6ae8927e1537654d88bf574a6958ac0bcd2d4de266b2a
18 .github/workflows/test.yml test.yml configuration 1426 2025-07-01T08:33:22.054430 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) ab09084a11be8adfc83fc6285c652fbe44a4584aa7d7d3658fcf0c57112d1640
19 .gitignore .gitignore other 566 2025-07-01T08:33:22.055006 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 358658b0faa8c3145e539421deba4ad0bac4a8ea2e608b2c2f867a822c67b36f
20 .python-version .python-version other 5 2024-12-02T16:41:37.012830 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 7b55f8e67b5623c4bef3fa691288da9437d79d3aba156de48d481db32ac7d16d
21 CHANGELOG.md CHANGELOG.md documentation 53647 2025-07-06T10:34:45.550847 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); semantic-release (semantic-release) 0b165eb18975ff253cde08f4687b7808ac6ce63b6d8c10912c495a76c9058d1a
22 CITATION.cff CITATION.cff other 302 2025-02-17T16:29:35.735146 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) bc4878cf6f80bb64655f3f93a5821ffe0924ad8311c784b5f81a36093fcffdda
23 CLA.md CLA.md legal 1342 2025-03-01T14:12:38.435132 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) c238c7ff4f2b8451c6bba1bbacd10b04fb7d41d9a9cd76a62644d8193791d169
24 CLAUDE.md CLAUDE.md legal 12310 2025-07-03T18:43:10.078147 Drew Cain 4 Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); Ikko Eltociear Ashimine (eltociear@gmail.com) 751f10c1a9a9c0ba2054d9c9cd36cf0989d6a347491fd505bb6504ee74b12900
25 CODE_OF_CONDUCT.md CODE_OF_CONDUCT.md documentation 509 2025-02-17T16:29:35.735238 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) b276b464e85eb1e5d9f09d90bba9573508a7bf81d2eeb38d0f25bd9f2fa66198
26 CONTRIBUTING.md CONTRIBUTING.md documentation 7329 2025-07-30T09:39:36.607158 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co) ca822fe39cc529bfc3822d317ab8a20fd141ec3ecb8c20de9e2eca3edce1c282
27 Dockerfile Dockerfile other 856 2025-07-01T08:33:22.056908 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) c45501905423990e31ffdb8ae4e976eca6423c6784905c1036bcc7795b70feef
28 LICENSE LICENSE legal 34523 2024-12-02T16:41:20.739392 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef
29 README.md README.md documentation 16261 2025-07-03T18:43:10.078281 Drew Cain 6 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Jason Noble (github+jasonn@jasonnoble.org); Marc Baiza (43151891+mbaiza27@users.noreply.github.com); Matias Forbord (codeluggage@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 2c4b82b30f49ab465617034fda999157450ed168f8e097fe8e9ca7ac65a197a7
30 SECURITY.md SECURITY.md documentation 303 2025-03-12T16:04:01.989927 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 96932268b8fe737577941b49e9dff5ff8ad59c11cc3f9a016989589ef5c98c1f
31 docker-compose.yml docker-compose.yml configuration 2534 2025-07-01T08:33:22.059281 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 4a9fdfd2f0d42b148fe021eee77fd5e772c7ba9c2fe5d0cc310c8c7a64257417
32 docs/AI Assistant Guide.md AI Assistant Guide.md documentation 16423 2025-07-01T08:33:22.060491 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c717869a3d356852077026591364ecb0589a6d53f9764af027c57d142ea7ff55
33 docs/Docker.md Docker.md documentation 9088 2025-07-01T08:33:22.060976 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) e13d3f50148353046b52d9d493986880f76f2914ef4efe1a759b2cc9d10913a4
34 justfile justfile other 5496 2025-07-30T09:39:36.608008 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com) a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
35 llms-install.md llms-install.md documentation 2307 2025-06-29T14:19:28.826375 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 674105eb85c8f1d59fc2ddc6840f2a2a3e293358687a305c37dffc8144304f97
36 memory.json memory.json configuration 90075 2025-06-29T14:19:28.827172 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 22d5eee995ca0548b7d9dd152f9a3690c42a514cbf38fea750b3ed2a17dd99c5
37 pyproject.toml pyproject.toml configuration 3279 2025-07-30T09:39:36.608791 Paul Hernandez 4 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); semantic-release (semantic-release) 428a5d64d4555dc7cd2a81b89a3444bdb1d2f3017257fbc9160bd0ae982718e1
38 smithery.yaml smithery.yaml configuration 433 2025-03-11T16:36:03.389716 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 63f13499cf4fee0751b5005f5bd85597dc1c7f9a881f4ec8147cc23e95e75c2d
39 src/basic_memory/__init__.py __init__.py source_code 256 2025-07-06T10:34:45.551291 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@gmail.com); semantic-release (semantic-release) c946490ab9d64957d25df37f721392024916f8a81ed838f70a8e938b1c2637db
40 src/basic_memory/alembic/alembic.ini alembic.ini configuration 3733 2025-02-23T17:31:23.198433 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 20466c9c5f026db66793006bebb2f328a3686c7bb34da40d52f33c3eca29e717
41 src/basic_memory/alembic/env.py env.py source_code 2838 2025-07-08T20:39:18.336650 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) e241d987eadfcd5011cbd9ddbecb833d306de87937759d81c08d37d04a6906b0
42 src/basic_memory/alembic/migrations.py migrations.py source_code 718 2025-06-21T00:01:59.613806 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 96b8873d70dd04b48d5c45b7413a54d1224ab9577557ef0dad5929377a9fb144
43 src/basic_memory/alembic/script.py.mako script.py.mako other 635 2025-02-17T16:29:35.739659 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 304a8bfb6a804e5493f5300e79882c70c9f5bb2e87512f4d16f0e097dddd323f
44 src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py 3dae7c7b1564_initial_schema.py source_code 4363 2025-06-21T00:01:59.595762 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 9536d69409ddd5eb3bc54f7d0e82607da45ed7f2ad7bc4cbf7cae2a9e28657cd
45 src/basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py 502b60eaa905_remove_required_from_entity_permalink.py source_code 1840 2025-06-21T00:01:59.604717 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 93ac584e660f33d468b3eec203b070641298c282bf82655d0beda7f050237681
46 src/basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py 5fe1ab1ccebe_add_projects_table.py source_code 4379 2025-06-21T00:01:59.601299 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) d82098f5aca3cdbada60c7229ea4ac262f527349eed9ef9e864509bacfaccf7e
47 src/basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py 647e7a75e2cd_project_constraint_fix.py source_code 4210 2025-07-01T08:33:22.063463 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 604ac5908a5976272f52297866d13abb14a99390590935d9fd37cf13e32049fa
48 src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py b3c3938bacdb_relation_to_name_unique_index.py source_code 1428 2025-06-21T00:01:59.598551 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 46c1b2990cdf4575752d234a8b28b494c8a54f15b5360c12f6347aef27b66a92
49 src/basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py cc7172b46608_update_search_index_schema.py source_code 3679 2025-06-21T00:01:59.610909 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 9036af47d42ac7dc12bb93f7a9de1266aff0688b031b550c4e0d92983a24b4c5
50 src/basic_memory/api/__init__.py __init__.py source_code 72 2025-06-21T00:01:59.579981 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) c02a63fb6d63d43d0acca97d425eae9cb055158d0ad6e1a9fc57926512adaa99
51 src/basic_memory/api/app.py app.py source_code 2897 2025-07-08T20:39:18.337081 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ad673ac2c09410af5830f6afdffaa28b0040480dacd52ff01750b8b2c066e8cf
52 src/basic_memory/api/routers/__init__.py __init__.py source_code 398 2025-06-21T00:01:59.549416 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 4443b908a43dea8e6fb4658009cb08c48a56c9b21449e29773cdd159b59cf014
53 src/basic_memory/api/routers/directory_router.py directory_router.py source_code 2054 2025-06-21T00:01:59.567371 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ac1407beec1f7d43a4d222af704b3642559c960bebf2eaf6e1ffe91f7fac5514
54 src/basic_memory/api/routers/importer_router.py importer_router.py source_code 4513 2025-06-21T00:01:59.561600 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) c45502a2b90f5a8f00174c9ad14adc2e65cd7fc0a33d9740a9d750207f0f3bea
55 src/basic_memory/api/routers/knowledge_router.py knowledge_router.py source_code 9738 2025-07-01T08:33:22.064119 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) e1d0ffb4fa5c246582ba844a11b417092de1a173567be55b7060bbc55fbc5681
56 src/basic_memory/api/routers/management_router.py management_router.py source_code 2730 2025-07-08T20:39:18.337438 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) cdbce294dcec6146c56c4dae15744cdf77039fd21b23eef7cbc0b5f9bfb5f4ee
57 src/basic_memory/api/routers/memory_router.py memory_router.py source_code 3003 2025-07-01T08:33:22.064510 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6bd0a7c775e0c1290efb6001173237c0cdcfa0c1b1bb27c9145a7b35f1596a97
58 src/basic_memory/api/routers/project_router.py project_router.py source_code 8444 2025-07-30T09:39:45.721369 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6539c69f1d506c65d14cc2c151876b19057f37d84d51a9e6f464ed47a4692648
59 src/basic_memory/api/routers/prompt_router.py prompt_router.py source_code 9948 2025-07-01T08:33:22.065221 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) e30c6aebe35112054933c37d0b462c3750005030fc9e44c23b35b2cd2a93485c
60 src/basic_memory/api/routers/resource_router.py resource_router.py source_code 8058 2025-06-21T00:01:59.570399 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 584244a84698ff24ca8f9f94fab5b891740a51c25f972920c08ebf83f478d5c9
61 src/basic_memory/api/routers/search_router.py search_router.py source_code 1226 2025-06-21T00:01:59.543020 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 183eb68e50904e22ff54db1d89b8be6f57fa1f8d0a096a3d497d8297b607e105
62 src/basic_memory/api/routers/utils.py utils.py source_code 5159 2025-07-03T18:43:10.079143 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 9e60f57da24e1dc9d64236c26a7a2353003dc618a77fbeb853c494aa3357a57c
63 src/basic_memory/api/template_loader.py template_loader.py source_code 8607 2025-06-21T00:01:59.576132 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 7b16d3797c89460c8b1664a335eae8c634fb5f60d6166d97e6a5156b776b6d83
64 src/basic_memory/cli/__init__.py __init__.py source_code 33 2025-06-21T00:01:59.456735 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 6ab70a2c05910e13c3ef1e6df34325be265e633c076741998a0cb2dcd295a069
65 src/basic_memory/cli/app.py app.py source_code 2268 2025-07-08T20:39:18.337708 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) f710670380d707c4458ad881f7a25bb3c5cdb2bcff59f139f37421264fe8f2f8
66 src/basic_memory/cli/commands/__init__.py __init__.py source_code 393 2025-07-08T20:39:18.338070 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) de8a23702f98fb844fa9f7fdbedc16ce24ff4f8baf05589c2f4a521cd8a55645
67 src/basic_memory/cli/commands/db.py db.py source_code 1480 2025-07-08T20:39:18.338326 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 704a1096d80ab9d12e247d029fe6223e935a0d0ceee7e615c020f46a720828e0
68 src/basic_memory/cli/commands/import_chatgpt.py import_chatgpt.py source_code 2856 2025-07-08T20:39:18.338787 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 8957cca3acab6351335624a518bdc19d956487e93d86dd1b6c2309e9d5850ae5
69 src/basic_memory/cli/commands/import_claude_conversations.py import_claude_conversations.py legal 2946 2025-07-08T20:39:18.339053 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7bc97838732bf00f4fb4a80e3ba4fdfbce8971ae85cc2ac902c3b3a3e110ae57
70 src/basic_memory/cli/commands/import_claude_projects.py import_claude_projects.py legal 2891 2025-07-08T20:39:18.339310 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6321577075801c99ad47a0cdc13b5aa3c9ca102a05ca8f06ae2a51125a8c43bc
71 src/basic_memory/cli/commands/import_memory_json.py import_memory_json.py source_code 3013 2025-07-30T09:39:45.721637 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7ea193f6720b2672290ad16c79a143a31d88a1d567838f12b9ebf9110a7c0338
72 src/basic_memory/cli/commands/mcp.py mcp.py source_code 2593 2025-07-08T20:39:18.340025 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 3e1be3d27b18ef4743204b7cf8a37fbc2a1a91fc1636c468e08cefcd8597bac6
73 src/basic_memory/cli/commands/project.py project.py source_code 13836 2025-07-30T09:39:45.721838 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) af3e6f30410ab48355f4b1a46aaa3aa5423cb0a0490f51447db1d25e09f39916
74 src/basic_memory/cli/commands/status.py status.py source_code 5809 2025-07-08T20:39:18.340324 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) b6d0ea237d714d054f69b24a2058beba74e224a2fa4a80a525d62a2aa2ea2d70
75 src/basic_memory/cli/commands/sync.py sync.py source_code 8213 2025-07-08T20:39:18.340627 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 703b3e73c4c33850c27f6f4c07abbf5d4ce5dc43f706bf0c43264bbb45f8b213
76 src/basic_memory/cli/commands/tool.py tool.py source_code 9540 2025-07-01T08:33:22.070903 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 9b2fa400b9f7921bf55b602f8bbdfa36b1ec7e4a5bc8fe7b9838b92e31f0a9ed
77 src/basic_memory/cli/main.py main.py source_code 465 2025-07-08T20:39:18.340868 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7a405b12bff98e319968526238ae15859df0362bba5a3857562fecca47b5c70d
78 src/basic_memory/config.py config.py source_code 12487 2025-07-30T09:39:45.722031 jope-bm 5 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
79 src/basic_memory/db.py db.py source_code 7428 2025-07-08T20:39:18.341550 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 25e053b0b3f9e8fa823b05fc099001efe8a3a4f6e0f80728e5079ba45aa406d8
80 src/basic_memory/deps.py deps.py source_code 12144 2025-07-08T20:39:18.341870 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) bce28cd9ca549b8afce0bc689cc1fb2dc80cd5b929f85114aa682bb8ae82411d
81 src/basic_memory/file_utils.py file_utils.py source_code 6700 2025-06-21T00:01:59.587200 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 79ac5328b2c46d3232fcc6ff22ffc39ade085c0252ad91958be2a7ae9c9c8b71
82 src/basic_memory/importers/__init__.py __init__.py source_code 795 2025-06-21T00:01:59.295471 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 0537015bdecfded85cb166b9c3db50b2f3aef329c70e0c36fbcb4f824099a21f
83 src/basic_memory/importers/base.py base.py source_code 2531 2025-06-21T00:01:59.305335 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 6b0c1efd4f827f348834aa0cea2ae8eebbb842a2e5b1f5f3747ced0ef79b9f13
84 src/basic_memory/importers/chatgpt_importer.py chatgpt_importer.py source_code 7372 2025-06-21T00:01:59.308956 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) dfa540b196ab23b5c4e6bfcac4da567873351cf7c78fa35f4c3b87e84c58e218
85 src/basic_memory/importers/claude_conversations_importer.py claude_conversations_importer.py legal 5725 2025-06-21T00:01:59.288540 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) a7bc9e872f4f81ce5f79fe9dd1a6fd0f00a6f020a2cb2664184a97f14eeb1dbc
86 src/basic_memory/importers/claude_projects_importer.py claude_projects_importer.py legal 5389 2025-06-21T00:01:59.291737 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) a452675fd9bb18ebf64eb4bd7f69ccd7e993b617931015a3c4ab700d67493863
87 src/basic_memory/importers/memory_json_importer.py memory_json_importer.py source_code 4631 2025-07-30T09:39:45.722316 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) bc7d045299f17ed9ad5cebff7b1423250ec28a11130e47a0ac48d3ab82bdeafc
88 src/basic_memory/importers/utils.py utils.py source_code 1792 2025-06-21T00:01:59.298428 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) ee7e22fd4812362394f62ed8944abe80738c1e3ab88131faf639f4a8ccfd6c89
89 src/basic_memory/markdown/__init__.py __init__.py source_code 501 2025-06-21T00:01:59.316337 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 0ddce2a025ad0e729aab4e411d82e02ffefc15ac041cba4b5e7a7e90f4957c87
90 src/basic_memory/markdown/entity_parser.py entity_parser.py source_code 4516 2025-06-21T00:01:59.321461 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 1b0d1177358645ccbadd170b3f62760dc9bd838d38c741978ab0da5836234d68
91 src/basic_memory/markdown/markdown_processor.py markdown_processor.py source_code 4968 2025-06-21T00:01:59.325241 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 4b99f2ebdceed9d96a3bbb562682eba4b4b383c7a64080eaec3bbba25a4952c9
92 src/basic_memory/markdown/plugins.py plugins.py source_code 6639 2025-06-21T00:01:59.312733 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 82d2332918e866ccaf06a2e954d9eb9b397f71b4d9e591a7f2472e5f1423464a
93 src/basic_memory/markdown/schemas.py schemas.py source_code 1896 2025-06-21T00:01:59.329982 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 7b2c580abd615725a62267257b46ac13922dfc30fa01192a681658bb528ae67e
94 src/basic_memory/markdown/utils.py utils.py source_code 3410 2025-07-03T18:43:10.079889 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 1b757f0d06e60e3ea276c0b2ea44fe1a156a895e093c1e607d6286e702bf4ae4
95 src/basic_memory/mcp/__init__.py __init__.py source_code 35 2025-06-21T00:01:59.404757 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 76c0ce84aaa361e21b0942db1c8c5f708b536eab9d12e120d4da7ce9eab41844
96 src/basic_memory/mcp/async_client.py async_client.py source_code 925 2025-07-30T09:39:45.722547 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 6531f4387f30a30a322197ff51d6eacae4b25a58c648095931b3d1145d1da303
97 src/basic_memory/mcp/project_session.py project_session.py source_code 4203 2025-07-08T20:39:18.342906 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 4da403ec93dec9063eeb8b05246e3501740d162cc77a067d3ce54ba43d690819
98 src/basic_memory/mcp/prompts/__init__.py __init__.py source_code 615 2025-07-08T20:39:18.343263 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) f8197d0e08f64c3f4f50b8f38203ea5eebcf123582472ed2f58834de1dbe53b0
99 src/basic_memory/mcp/prompts/ai_assistant_guide.py ai_assistant_guide.py source_code 821 2025-06-21T00:13:53.262832 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) f13239c4e6e2455730bfac3d6f2d71407957d3086fc84efe2c6b22a833114c58
100 src/basic_memory/mcp/prompts/continue_conversation.py continue_conversation.py source_code 1998 2025-06-21T00:13:53.263309 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) aec9a50b657b7bb1ba0c02b42bcdb9bc5b0f2a0b1143bd361c5ce7ea59076833
101 src/basic_memory/mcp/prompts/recent_activity.py recent_activity.py source_code 3311 2025-07-01T08:33:22.073986 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) d2fd5cddbd927430f15d5b85f1e3a3368a18cb4e2e4587a5d29749d2b9e0830e
102 src/basic_memory/mcp/prompts/search.py search.py source_code 1692 2025-06-21T00:13:53.264093 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 9dbf3c319cbdb5d5bf32608b51522d8ae92b2dd6f7c441d62efd0954b525738a
103 src/basic_memory/mcp/prompts/utils.py utils.py source_code 5431 2025-06-21T00:01:59.425042 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 55a72b6eac18b724a9225608aca1e8e6cea3b684ccb1c609aab1511f7ce90ba4
104 src/basic_memory/mcp/resources/ai_assistant_guide.md ai_assistant_guide.md documentation 14777 2025-03-28T19:12:38.653627 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) aa76160e46256fe2662ae3a86799659916acfede1d5835c1fe2f0b136efb2e0b
105 src/basic_memory/mcp/resources/project_info.py project_info.py source_code 1906 2025-06-21T00:13:53.264918 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 2dc5244f1e225c17d4e8ba784d5721efc3aa2e8a5b3b2e258f22a77ebe155eca
106 src/basic_memory/mcp/server.py server.py source_code 1248 2025-07-08T20:39:18.343419 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 0a910cae27d61ea923e90fbe2d4b5c7cacf3b29d3e156f0b7f77872ab973c761
107 src/basic_memory/mcp/tools/__init__.py __init__.py source_code 1619 2025-07-03T18:43:10.080029 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co) 872b771dd52ec3b763645a2baf6e10a70f049ce3106830a6d3f053b3e09a5fe6
108 src/basic_memory/mcp/tools/build_context.py build_context.py source_code 4611 2025-07-03T18:43:10.080426 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 724280b77b8f5f3e61cd3fdeebc3ee66e2bcfedaaea3638e6a2e2e33fcb1978e
109 src/basic_memory/mcp/tools/canvas.py canvas.py source_code 3738 2025-07-01T08:33:22.076204 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) db617d1bd81f3dbfa5f22d41e6b6b825afe1f73618f37acf63d9830390b98246
110 src/basic_memory/mcp/tools/delete_note.py delete_note.py source_code 7346 2025-07-01T08:33:22.076726 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) b52c9173f5600662f255e7a70a5c17d5293ef8b29c19a8403334d7da62b65c8b
111 src/basic_memory/mcp/tools/edit_note.py edit_note.py source_code 13570 2025-07-01T08:33:22.077181 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) ab8c7e7fbfa3fe5fb0ce6d7bf805454f5fd6182a340aae25237b1e6127b6d5a6
112 src/basic_memory/mcp/tools/list_directory.py list_directory.py source_code 5236 2025-06-21T00:13:53.267453 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) f85c43b02aeee580f4d8ce2a9100c0bab1095b245a0bb608e1847ace0d1ab51f
113 src/basic_memory/mcp/tools/move_note.py move_note.py source_code 17541 2025-07-30T09:39:45.722919 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 5ef27d9baaebc2c5656fc49c4798a4e4e40a70b6cd9d1e761dbcae021bad558c
114 src/basic_memory/mcp/tools/project_management.py project_management.py source_code 12944 2025-07-06T10:34:45.552403 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co) cdac736d65129f689a85c6b82f8e043bc84a31657ead7a8331771171eeaa860f
115 src/basic_memory/mcp/tools/read_content.py read_content.py source_code 9125 2025-07-30T09:39:45.723469 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 96a13addac507fd874394dfa2e1e5ed71af5eb89d0872c1d68cacf9cac3f8724
116 src/basic_memory/mcp/tools/read_note.py read_note.py source_code 8132 2025-07-30T09:39:45.723828 jope-bm 5 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) dff53ce02b16d9c5fca315b3f09bb70867a89d614b0ef3f0548dce87978048ee
117 src/basic_memory/mcp/tools/recent_activity.py recent_activity.py source_code 4833 2025-06-21T00:13:53.269120 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 5d58cd2402679b1bf3c7dfcbb3503e40e776c9347ba499524d3b91c528af98de
118 src/basic_memory/mcp/tools/search.py search.py source_code 15883 2025-07-03T18:43:10.081557 Drew Cain 4 Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 8519b0057468c4450eb543a2f561bcd0d7cb96e1ce1b95e948e02b309ba6b7ca
119 src/basic_memory/mcp/tools/sync_status.py sync_status.py source_code 10517 2025-07-08T20:39:18.344231 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 73dd73b186eaec8b82e6b17e39a67c27082d24e0653dc4e4b35712bd83b80a22
120 src/basic_memory/mcp/tools/utils.py utils.py source_code 20892 2025-07-30T09:39:36.616253 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) a95004911e276822eba88a3fef15c5b9ba861b1ca9a2ecfe0c1e3fb2f4ef0116
121 src/basic_memory/mcp/tools/view_note.py view_note.py source_code 2499 2025-07-01T08:33:22.080024 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 75d357c72113b1d0394987e521a4158ff09b77b23b08b56cddab5144331b1a68
122 src/basic_memory/mcp/tools/write_note.py write_note.py source_code 6612 2025-07-30T09:39:45.723985 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 771396fb49d397feb5f7e36321d4d233c22c4d1b53414912c849957c6e96fc83
123 src/basic_memory/models/__init__.py __init__.py source_code 333 2025-06-21T00:01:59.437212 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 8f40b876d162f85384690291f1d416106f9d26d750d793414e2260e276c85cd5
124 src/basic_memory/models/base.py base.py source_code 225 2025-06-21T00:01:59.447249 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) e2101727c084d519e32a16f6de53ddf9033b1bf15721d4e8c0c27d6d18b1b295
125 src/basic_memory/models/knowledge.py knowledge.py source_code 7087 2025-06-21T00:01:59.444376 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 005c5f292f1f45ae372aadc48c9080b9fa6d7b854d0bb7ecf587ec843ac1e28d
126 src/basic_memory/models/project.py project.py source_code 2773 2025-07-01T08:33:22.080907 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) a14ad06943aeeff9ae4a5fa2dfc0e1d07ce6085acc02dc20c402c3513b7d9397
127 src/basic_memory/models/search.py search.py source_code 1300 2025-06-21T00:01:59.440923 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 3e143cc38b5a0294af8e1d43a4f841e1c1fd193b76136a68f83159ce19e86646
128 src/basic_memory/repository/__init__.py __init__.py source_code 327 2025-06-21T00:01:59.256363 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 3162bea3c42292accea5ee52c8f6ca4368a8079056034529cfae6d820f84d035
129 src/basic_memory/repository/entity_repository.py entity_repository.py source_code 10405 2025-07-03T18:43:10.082570 Drew Cain 3 Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) e2a8d1eba6c8d64bc61d7168df0fe8c29a670858bf9daeac464a0f440273fae0
130 src/basic_memory/repository/observation_repository.py observation_repository.py source_code 2943 2025-06-21T00:01:59.269646 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) aa132f1cb4a36a84f715afdc40e2ac4f98d83e3eba1974b2b4404cc0b020ca04
131 src/basic_memory/repository/project_info_repository.py project_info_repository.py source_code 338 2025-06-21T00:01:59.252582 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) f172d50182a405643a19b2a3d62a80f4e2b41463c775394eb3b66d7de51ff6df
132 src/basic_memory/repository/project_repository.py project_repository.py source_code 3803 2025-07-30T09:39:45.724344 jope-bm 2 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 9083ba214b2bcaaf98164d9c33cda7add52bc77f7269ecb6e43cdd5088728b7c
133 src/basic_memory/repository/relation_repository.py relation_repository.py source_code 3179 2025-06-21T00:01:59.246745 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) cfb3a8e59cff27e063e91bd00e949647bdd92e4d9fc46edeee3acc6de15e26f4
134 src/basic_memory/repository/repository.py repository.py source_code 15224 2025-06-21T00:01:59.273355 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 3096fe71bf106506cbf86aeafe2aafe0aabbe5a5f6c90a212c8aa1e53e1f171c
135 src/basic_memory/repository/search_repository.py search_repository.py source_code 21936 2025-07-03T18:43:10.082844 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) a972f73d1b71dac5770e8eb3793c6c9ac44e4e792f9dab528f8c4e6c6a80bcaa
136 src/basic_memory/schemas/__init__.py __init__.py source_code 1674 2025-06-21T00:01:59.514555 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 98480815c75379bfafe32d2090e87fa40e73caa19b664fbd5db5ea9528ba58cb
137 src/basic_memory/schemas/base.py base.py source_code 6738 2025-07-01T08:33:22.082193 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 171f7b0c4ab33abef2f7379eb1e3bda950586ce7edff83907fd12255f84e267e
138 src/basic_memory/schemas/delete.py delete.py source_code 1180 2025-06-21T00:01:59.502563 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 50047624af7d58c8f780ffb2a065a51c3dde64492f9534917fc42860813e59fc
139 src/basic_memory/schemas/directory.py directory.py source_code 881 2025-06-21T00:01:59.529023 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 17dfcbac9a91a9bfe43b4f060ca2735cb6f69e16d81b63dd554a397830ff29fe
140 src/basic_memory/schemas/importer.py importer.py source_code 693 2025-07-30T09:39:45.724633 jope-bm 2 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ac33df423ca32b28ce7b6ea9c29d541f8783a86c0c29f78db35163bf93f139cd
141 src/basic_memory/schemas/memory.py memory.py source_code 5833 2025-07-03T18:43:10.083167 Drew Cain 3 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) acb4a953a553feca672c4895798a7d948ec51f922f75b66dd7d55716e3bebed3
142 src/basic_memory/schemas/project_info.py project_info.py source_code 7047 2025-07-01T08:33:22.083248 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7dc3635297b6e7fe6e3262b2d78da26f7a79a846a4cecd5624f2e4828979cf2c
143 src/basic_memory/schemas/prompt.py prompt.py source_code 3648 2025-06-21T00:01:59.525753 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 4a92157d9a6b413f04e6e3f8d23dc2a417369c729f9703a8de2643ec114f2071
144 src/basic_memory/schemas/request.py request.py source_code 3760 2025-06-21T00:01:59.511789 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 32fe44beb2d99452223ebf1d3a3a3fe105ef92c79885023b71dfd7db30ecc503
145 src/basic_memory/schemas/response.py response.py source_code 6450 2025-07-03T18:43:10.083478 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 5eea4660a2abe48d83ed083d1c2483fdcfb403e0b504f03c14d22f1cae866abb
146 src/basic_memory/schemas/search.py search.py source_code 3657 2025-06-21T00:01:59.522804 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) cb032c0c60102b6b0ed934f995cf9d6bf93aece296d71d537a75e8ae61c75afe
147 src/basic_memory/services/__init__.py __init__.py source_code 259 2025-06-21T00:01:59.655059 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 5c6b7c597ddf5ffd0af4bdfb32ccbc1c5f2794c658206dee43a9945fafe226d8
148 src/basic_memory/services/context_service.py context_service.py source_code 14462 2025-06-21T00:01:59.672086 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (paulmh@gmail.com) e1178b005e6a89f03d6b239e3c6b152aeb30d535a3f280734440a3aec16228f2
149 src/basic_memory/services/directory_service.py directory_service.py source_code 6017 2025-06-21T00:01:59.648501 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) fd838f5ec79033892777b3c8140868f4b57f13e1658d513959524a434b9f959b
150 src/basic_memory/services/entity_service.py entity_service.py source_code 30440 2025-07-03T18:43:10.083956 Paul Hernandez 4 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 7cd5163eca6b8a0772e838c8c869e478166763cd6a2d745b0b9aa5c25a65baee
151 src/basic_memory/services/exceptions.py exceptions.py source_code 390 2025-06-21T00:01:59.668908 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) a158d0af9d1742a9c5ab5f8c34a062948d9286d1c3c72a5abf20e4d547b21e1c
152 src/basic_memory/services/file_service.py file_service.py source_code 10059 2025-06-21T00:01:59.641785 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 8c2ae69c4913438b7d1c5ecbfcce812fbb5d0ea8e3cedcbd8694e8f40cf086f3
153 src/basic_memory/services/initialization.py initialization.py source_code 6715 2025-07-08T20:39:18.344482 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 4dde58b799cf4ac90631e59949b55b3355a93bdf99df0d9c2408c0cea67e10c4
154 src/basic_memory/services/link_resolver.py link_resolver.py source_code 4594 2025-07-01T08:33:22.084908 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) d7efd516cbea753e6b5411def09aeaeb7539f5743487119ecf163c736e138a8c
155 src/basic_memory/services/project_service.py project_service.py source_code 30125 2025-07-30T09:39:45.725079 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) bce95c00aa9a1da0a8c8090ee34b5b1494207f8c99e6c25de4ec43fddc38f6be
156 src/basic_memory/services/search_service.py search_service.py source_code 12782 2025-06-21T00:01:59.665439 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7392b2d2e7f3ed83c5807855ccd450e0e79c17f254aedee700bce9323a1b5b83
157 src/basic_memory/services/service.py service.py source_code 336 2025-06-21T00:01:59.636167 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 57e77ff20395d3bcc62100e92fe2acaacdd937d977a9fdc764e2b57ff60d4da8
158 src/basic_memory/services/sync_status_service.py sync_status_service.py source_code 7504 2025-07-03T18:43:10.084229 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 0a025d689e8e16f1632872104952105fc904537f3d32bcff592eb1f1dc76fbb7
159 src/basic_memory/sync/__init__.py __init__.py source_code 156 2025-06-21T00:01:59.621137 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 0951e0b981f8e7b876bb6c6833c2af3a29490bbd5726567ea9487c947723623e
160 src/basic_memory/sync/background_sync.py background_sync.py source_code 726 2025-07-08T20:39:18.345062 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 549af64ae91128b76c6df07ef517b82de85ca4ad796be44b5c0141fac01d4513
161 src/basic_memory/sync/sync_service.py sync_service.py source_code 23387 2025-07-01T08:33:22.086507 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 0310b927561370f593980d07773bce641b618b8fbf2d9e3890d174290a0344fc
162 src/basic_memory/sync/watch_service.py watch_service.py source_code 15316 2025-06-26T09:28:51.859209 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 240ba6ac7523575945f4db442b7da3820d26c9605f2d7a2d357c4e35e215f523
163 src/basic_memory/templates/prompts/continue_conversation.hbs continue_conversation.hbs templates 3076 2025-07-01T08:33:22.086792 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) b6bac31d25c0e52d0909b2273285092f4e31bc219107f92ed511cd407b65e992
164 src/basic_memory/templates/prompts/search.hbs search.hbs templates 3098 2025-05-25T10:07:54.520119 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 1f570222c1caa78542d46ac7d8a79407ca467b9bd714fa9bd953e8b72a667820
165 src/basic_memory/utils.py utils.py source_code 8654 2025-07-30T09:39:45.725428 jope-bm 5 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); andyxinweiminicloud (andyxinweimin263@icloud.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
166 test-int/conftest.py conftest.py source_code 8203 2025-07-08T20:39:18.345735 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 7231e1bc76cddc7c94433e853825dc0165b103bedf996c2c24eda6ae32fff5ad
167 test-int/mcp/test_build_context_validation.py test_build_context_validation.py source_code 6469 2025-07-08T20:39:18.346021 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) ee61d6b15cee0194fd74c1bcf9361d5a839f6081c7d571ef081da8fcefe1bbf2
168 test-int/mcp/test_delete_note_integration.py test_delete_note_integration.py source_code 13175 2025-07-08T20:39:18.346366 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 497495249b8bcf0af177d8705c6899875460707697980434e560cfe56f86feb3
169 test-int/mcp/test_edit_note_integration.py test_edit_note_integration.py source_code 19822 2025-07-08T20:39:18.346730 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 9ba704badd05808aee0b5a686b7e250e1c865229ec92dab561cba5287b6f6fc6
170 test-int/mcp/test_list_directory_integration.py test_list_directory_integration.py source_code 14744 2025-07-08T20:39:18.347106 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 2c4336a59de63f17ed252bb52d1330ebca901ad83bfebc9e5aa67f132a3c77c6
171 test-int/mcp/test_move_note_integration.py test_move_note_integration.py source_code 19825 2025-07-08T20:39:18.347344 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) cbb17688d0f9551306a7520b059ace7f7dfba70fc20bf0ec98b1148000d7ad7d
172 test-int/mcp/test_project_management_integration.py test_project_management_integration.py source_code 33175 2025-07-08T20:39:18.347658 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 93a43cd699453a7602a5def839740ba5027ab914eba3e7b66a026860ed6bd567
173 test-int/mcp/test_project_state_sync_integration.py test_project_state_sync_integration.py source_code 7728 2025-07-08T20:39:18.347907 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 80b0f5b5cd359498dcbd3af7dcb6194afa4582d8efb3faa38d28675ee45e0748
174 test-int/mcp/test_read_content_integration.py test_read_content_integration.py source_code 11187 2025-07-08T20:39:18.348233 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 6955dd343b40df088ac79175e52723501d83796335a16e43b7f58e16e757e1b8
175 test-int/mcp/test_read_note_integration.py test_read_note_integration.py source_code 1381 2025-07-08T20:39:18.348463 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) b93050da68d0e9d47d46879a386567f688399e28390f4eb31ee37353dc27514b
176 test-int/mcp/test_search_integration.py test_search_integration.py source_code 14690 2025-07-08T20:39:18.348959 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) e0957ada9dd9b12eae5843a37d241ec9293ca929f18281e31197dae36bff589d
177 test-int/mcp/test_write_note_integration.py test_write_note_integration.py source_code 9472 2025-07-08T20:39:18.349398 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) b2b56fee8351168d0c65640c295d969dc4575d9bea87927b2dc07e591cabdff3
178 tests/Non-MarkdownFileSupport.pdf Non-MarkdownFileSupport.pdf other 106751 2025-02-23T17:31:23.206428 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 4aa96f9d0ee830ef6a1ec92243f302bbe2fa8971b84945f4eb5e276bf1ffde96
179 tests/Screenshot.png Screenshot.png other 190076 2025-02-23T17:31:23.207771 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) dd82612fe013c39810d28ae0f2bbcca6ab2c9925b035d7720bb02493ddf5d0a9
180 tests/__init__.py __init__.py source_code 141 2025-06-21T00:01:46.216456 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 2ac5605e69ad8829e96b3689632536539f1cdd3149a29c1903f25af0793a5d65
181 tests/api/conftest.py conftest.py source_code 1404 2025-06-21T00:01:46.359728 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) ea46cde47a37c1f43fe079e48f5d0de8007155a0187f4445abc4f2ffde9c1791
182 tests/api/test_async_client.py test_async_client.py source_code 1382 2025-07-30T09:39:45.725666 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) a2f96226a75e5cff2326ca48ddccea4915964d83e529f7b99958bc233ccdc31a
183 tests/api/test_continue_conversation_template.py test_continue_conversation_template.py source_code 5152 2025-06-21T00:01:46.390901 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) cca08b0291db0ad693c60d8dc203660021feab46d9eb530be7f67e0ebb52c1d3
184 tests/api/test_directory_router.py test_directory_router.py source_code 9376 2025-06-21T00:01:46.356659 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 428626589f0010512481ba495368b0bf2b9617b22764e588eaa3562e86da4d37
185 tests/api/test_importer_router.py test_importer_router.py source_code 16578 2025-06-21T00:01:46.368070 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 03edf6e7404c7350afe76e54ea2bf0d66f668bcdb49f59702067a67b3694309d
186 tests/api/test_knowledge_router.py test_knowledge_router.py source_code 45771 2025-07-01T08:33:22.090490 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 43a5f91ae1fb9038e65c09701336fbdc4dbf40c8319c94727e6312fda9d6d56e
187 tests/api/test_management_router.py test_management_router.py source_code 6220 2025-06-21T00:01:46.402764 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) f6a3f41f263f10c08281525571e2e39fc9ae818ebb7e9895e3f3f3115ea05677
188 tests/api/test_memory_router.py test_memory_router.py source_code 5555 2025-06-21T00:01:46.399667 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) 2b8782015c893081fc67b7f13a7255d4e3dc45b27705a524f94918a339e2d486
189 tests/api/test_project_router.py test_project_router.py source_code 13288 2025-07-30T09:39:45.725847 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) af85d0870069d3831a9214372853994e5d12dc910e37298eb9958f72f6a8bb31
190 tests/api/test_project_router_operations.py test_project_router_operations.py source_code 1812 2025-06-21T00:01:46.381705 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) afd40879de3d7c508cca0feeae3cb7a8f3afcfbb06f59a6104189b1e3291376a
191 tests/api/test_prompt_router.py test_prompt_router.py source_code 5046 2025-06-21T00:01:46.396588 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 9115995b7dd8b44d8a9c06c5e1bbb177618286675a769889e35ac16f26fd9897
192 tests/api/test_resource_router.py test_resource_router.py source_code 13960 2025-06-21T00:01:46.384919 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ad3cb2cabaeab82a7e64253ceede7c1791e2bd70c92f60bc5f3be939292ca22d
193 tests/api/test_search_router.py test_search_router.py source_code 6409 2025-06-21T00:01:46.375427 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (phernandez@basicmachines.co) ba3e3809795e72db9dd9c8c729048bdbbbdb897c2a5aa8b175112b5d4b56d152
194 tests/api/test_search_template.py test_search_template.py source_code 5258 2025-06-21T00:01:46.378721 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 8f0d16a1de947d0466d96afed02e695f7648adac70b9ba99753250068207a686
195 tests/api/test_template_loader.py test_template_loader.py source_code 8035 2025-06-21T00:01:46.406694 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 4fd7c6c4862cc0d877d29244ecdc0b52a0693fc4574a9667663bee36bfd36b62
196 tests/api/test_template_loader_helpers.py test_template_loader_helpers.py source_code 7445 2025-06-21T00:01:46.372114 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 8dd35f11c9b3f2d96ac36263edf4430308728c04c9da543b1cb845b96ab20aa7
197 tests/cli/conftest.py conftest.py source_code 1185 2025-07-01T08:33:22.090848 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c3c73da7ad73224931e1fba4ebd70792221be607bfb1488bb5c9420bba03bdb8
198 tests/cli/test_cli_tools.py test_cli_tools.py source_code 13528 2025-06-21T00:01:46.318964 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) befb8cab50132e26a1b13b82ce8e93a3258282cf157a4230214bfcefeae65102
199 tests/cli/test_import_chatgpt.py test_import_chatgpt.py source_code 6719 2025-07-08T20:39:18.350234 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) df447335545248e2fb2417af33f8cc2f0143bd413a1783e41f89605de305b7c8
200 tests/cli/test_import_claude_conversations.py test_import_claude_conversations.py legal 5028 2025-07-08T20:39:18.350607 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ac4b3b9dfc331fa7fa32a2941c027ff8b6585a94a5ccfaaf0a4affe5cf19c00c
201 tests/cli/test_import_claude_projects.py test_import_claude_projects.py legal 4585 2025-07-08T20:39:18.350885 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7eaab3d491d50ca59c8ba015ecd5740a5feae94d970ee17559c6df9d8558c2cc
202 tests/cli/test_import_memory_json.py test_import_memory_json.py source_code 4891 2025-07-30T09:39:45.726290 jope-bm 3 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) d6734ce059dacf97bc764d848b2e2a6ba52fa96871bc331ec9d2ddc60b766bb6
203 tests/cli/test_project_commands.py test_project_commands.py source_code 6578 2025-07-30T09:39:45.726703 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 51e2ac7ff44b0d1a1db6da406c5c2e6aebceb1b63a1e8d5ab51bbaa834507157
204 tests/cli/test_project_info.py test_project_info.py source_code 3874 2025-07-01T08:33:22.091600 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 0b08950e95a5f276f14c56f6fe99af4d41b8f04548e9b7746f807c5ac4155578
205 tests/cli/test_status.py test_status.py source_code 4160 2025-07-01T08:33:22.091978 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) a1ff89fdac4bf57ba03be92ac79b8ca3210af0b1132d5a2bd0fc0c253fcbf988
206 tests/cli/test_sync.py test_sync.py source_code 3864 2025-07-08T20:43:47.816788 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 77e0f6b42b584a5acfda3c2ff194ff5e71ca57a40cb4484d563f5482bbbb4c0f
207 tests/cli/test_version.py test_version.py source_code 289 2025-06-21T00:01:46.305368 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) db95665178a32d391bd83011d073db269cefba9da8a4dc4d3e392d47bc0e0a8f
208 tests/conftest.py conftest.py source_code 15286 2025-07-08T20:39:18.351368 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) c6f38f103ce58144ffe4562f0c47dad849974e96327f02805e854447e19bc411
209 tests/importers/test_importer_base.py test_importer_base.py source_code 4329 2025-07-08T20:39:00.549594 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) da872a073f2b90fda3c98c9f44cf45884f4f682d91ec1b1f931d42c34eee1939
210 tests/importers/test_importer_utils.py test_importer_utils.py source_code 1827 2025-06-21T00:01:46.184343 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) cbee630c855ceb1de764ac0cdb1d5d781c16cd3a934d67166adc235c98f363d7
211 tests/markdown/__init__.py __init__.py source_code 0 2025-06-21T00:01:46.196570 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
212 tests/markdown/test_entity_parser.py test_entity_parser.py source_code 8669 2025-06-21T00:01:46.206579 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 496fdab6fe164634f8223f827585a4c0dedd30ccea7b0b69dc79004ad0996de2
213 tests/markdown/test_markdown_plugins.py test_markdown_plugins.py source_code 5139 2025-06-21T00:01:46.200759 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 9624d73462aadcd47fb0edd0d87880959a8982eecfa39529f21e3b292c34cb83
214 tests/markdown/test_markdown_processor.py test_markdown_processor.py source_code 5728 2025-06-21T00:01:46.194175 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 35042aa57dd2aeed848c624aa7f69decfb18e83c3b217eca1fdacfc4a10831db
215 tests/markdown/test_observation_edge_cases.py test_observation_edge_cases.py source_code 4295 2025-06-21T00:01:46.209598 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 6f1ab7684bfa8b02af0e0c42b3d7aed91fcd0b1c6288cbc9b6d6943c166979fa
216 tests/markdown/test_parser_edge_cases.py test_parser_edge_cases.py source_code 5142 2025-06-21T00:01:46.213291 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) d511d7d1187e3c1d3a8d3e0ab9efc9e68e84115c46811e7a9dc21eca9a87d520
217 tests/markdown/test_relation_edge_cases.py test_relation_edge_cases.py source_code 3952 2025-06-21T00:01:46.203846 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) f8e2016308f84139342cc37ec6e389ffe52d7eb46f998ee327c7f0d5bba3ad9b
218 tests/markdown/test_task_detection.py test_task_detection.py source_code 401 2025-06-21T00:01:46.191444 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 37fe91a689742033a0eb76723557e31c2a85e591133383b3887572cb714db2a5
219 tests/mcp/conftest.py conftest.py source_code 1761 2025-07-08T20:39:18.351554 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 350b2ca42c52ef76e002deddb65fc0eccb9f8989bd26078ea1c100e3b93bae3f
220 tests/mcp/test_prompts.py test_prompts.py source_code 5989 2025-07-01T08:33:22.093691 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) e3b2ee5b4bc120b5043225ebdbf73eadd71be9c3d9d8e1fdd66a24a8ff11a780
221 tests/mcp/test_resource_project_info.py test_resource_project_info.py source_code 4885 2025-07-01T08:33:22.094085 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6a043736789b239c8d4ae00d1a5332a0fc3bed4038fe3388970ba97dc7431a93
222 tests/mcp/test_resources.py test_resources.py source_code 549 2025-07-01T08:33:22.094481 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 77a6396b9b8a9d3b3d202e438243ca6178d87fd7680c9fc063ee013f2042aa4c
223 tests/mcp/test_tool_build_context.py test_tool_build_context.py source_code 3714 2025-07-01T08:33:22.094661 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 5f0a39fdb652605335ed54aa91fe994262441bb7ba46f381c46ebca882fc23f2
224 tests/mcp/test_tool_canvas.py test_tool_canvas.py source_code 7997 2025-07-01T08:33:22.094982 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c517bb1f9edc77b76fbeb483dfcb2dd57de9f0b80a10738708bc90cbe388dc9e
225 tests/mcp/test_tool_delete_note.py test_tool_delete_note.py source_code 4208 2025-07-01T08:33:22.095578 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 4cbf3ba4313438651b0608d5b25faf1400f7bd6a78af630351aecd4457800895
226 tests/mcp/test_tool_edit_note.py test_tool_edit_note.py source_code 13503 2025-07-03T18:43:10.084890 Drew Cain 2 Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) dd441969aa54b3739f2d9202d3feaf46aee807ec51e01a13ef41eda52743944f
227 tests/mcp/test_tool_list_directory.py test_tool_list_directory.py source_code 7730 2025-07-01T08:33:22.096737 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) cfbb5c06bcfe74ea60f31051eb9b03ac80b86232b053c188bf3b172132f7c0f5
228 tests/mcp/test_tool_move_note.py test_tool_move_note.py source_code 25851 2025-07-30T09:39:45.727347 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co) c9f95158503fa42e37532aaa35027538fa49cea718d059d570cf547d31f01043
229 tests/mcp/test_tool_read_content.py test_tool_read_content.py source_code 19442 2025-07-30T09:39:45.727526 jope-bm 1 jope-bm (joe@basicmemory.com) 1817e32f997b06dbdabf02864d0c70584c1cd80bf1fae8dfbef649de79434bc5
230 tests/mcp/test_tool_read_note.py test_tool_read_note.py source_code 22991 2025-07-30T09:39:45.727746 jope-bm 4 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 4e13ea9ff977525aa62b6a8c22505ffe989329dc3e7d07b71e044cf43ef3ddfd
231 tests/mcp/test_tool_recent_activity.py test_tool_recent_activity.py source_code 3983 2025-07-01T08:33:22.098561 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 45414152cd45334b84df56f653146b7bd2939907548e7245a37207fdccfc4c87
232 tests/mcp/test_tool_resource.py test_tool_resource.py source_code 6782 2025-07-01T08:33:22.098948 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 3916ae1c4d458eee53d2c570b9bbb708ecd617de5a145c3a7552389954bc9195
233 tests/mcp/test_tool_search.py test_tool_search.py source_code 10959 2025-07-03T18:43:10.085177 Drew Cain 3 Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 07839cf8e89d74ba05ec1dc55fa842444c1f0cc3d1587823b0ac3458ad89e87c
234 tests/mcp/test_tool_sync_status.py test_tool_sync_status.py source_code 6331 2025-07-01T08:33:22.100021 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 3f3566df5b9f3c66bb9b3bd77d4c05825480d0e85015305cb1b7ab9429c7a7ec
235 tests/mcp/test_tool_utils.py test_tool_utils.py source_code 9228 2025-07-01T08:33:22.100561 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) b50789f7a78b0169f52dc8bd16e262510ed4a0ac130fce698d4be84af1fe2d31
236 tests/mcp/test_tool_view_note.py test_tool_view_note.py source_code 10004 2025-07-01T08:33:22.101164 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 385535ce791c3ab55db827cbcf27ea25ae85faf3a027f52df4eea052cede7f36
237 tests/mcp/test_tool_write_note.py test_tool_write_note.py source_code 35136 2025-07-30T09:39:45.728137 jope-bm 3 jope-bm (joe@basicmemory.com); Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
238 tests/repository/test_entity_repository.py test_entity_repository.py source_code 15498 2025-06-21T00:01:46.176265 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 8f5d1ff6cff7b640993ff73f463b1e7801a4dd69640fc55fa23dc58b865e9943
239 tests/repository/test_entity_repository_upsert.py test_entity_repository_upsert.py source_code 16983 2025-07-03T18:43:10.085482 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com) ee0ecd6f4ff8b31838bd6de6fb1018af437e02813d8315c06f4e20a6b78a0572
240 tests/repository/test_observation_repository.py test_observation_repository.py source_code 11786 2025-06-21T00:01:46.158862 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) 3006d392ad94dcc9b3ae031d63a1af6935624534389dd025de70d4c9e112aa0b
241 tests/repository/test_project_info_repository.py test_project_info_repository.py source_code 1186 2025-06-21T00:01:46.161876 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) dbce7ae7350403cbc37343c2e3e68e4d2255e247006aaa6c495a7ce8bfddea7d
242 tests/repository/test_project_repository.py test_project_repository.py source_code 10511 2025-07-30T09:39:45.728536 jope-bm 2 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 279a3324635b1b515c18003bd1875cf230fef1192cadef9ca426846783717e75
243 tests/repository/test_relation_repository.py test_relation_repository.py source_code 11980 2025-06-21T00:01:46.167567 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) fcdbdf40f7b56145b98eedde5ead64ef6187e859adde48cc09f5a97754757428
244 tests/repository/test_repository.py test_repository.py source_code 5996 2025-06-21T00:01:46.152691 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 3a6d4ca0b7a438a30ae504f5eac22874d4e1a937a4246e90efad46c90f0e1c95
245 tests/repository/test_search_repository.py test_search_repository.py source_code 25471 2025-07-03T18:43:10.086041 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 805b5cc55a53ae9d6e86c0de735c7f38ac9fd92c23194b0150fa3e7f5507a146
246 tests/schemas/test_memory_url.py test_memory_url.py source_code 2000 2025-06-21T00:01:46.346515 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 946fa6c48534f41c8b9cc27bbad164d3e3673a79afa84fbb439fae4cd26c504b
247 tests/schemas/test_memory_url_validation.py test_memory_url_validation.py source_code 9338 2025-07-01T08:33:22.103247 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 7054c3a3cdb8e05d2ffd254ed298908ff3d48a0a3927d960975eae1ab9beeb38
248 tests/schemas/test_schemas.py test_schemas.py source_code 16880 2025-07-03T18:43:10.086337 Drew Cain 3 Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 2c746a00c340f063c160ace67569955a300a294040949695c79fd4ae124fb611
249 tests/schemas/test_search.py test_search.py source_code 3264 2025-06-21T00:01:46.343685 github-actions[bot] (AI Assistant) 2 github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 7437775d1b4a61a438cbabe3dbb1becd2adc4dbcc0c24595830c0843fa7a3166
250 tests/services/test_context_service.py test_context_service.py source_code 8833 2025-06-21T00:01:46.446200 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) ab49d21a05d5364e7e8a2f00b07aabe1109b7417698e0acd92792e2cb5f22732
251 tests/services/test_directory_service.py test_directory_service.py source_code 6854 2025-06-21T00:01:46.457258 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) a0c765fb6bd1a75b6ba4592b1df344c48273b6ab03ba6bd15d2b5e840eb2d521
252 tests/services/test_entity_service.py test_entity_service.py source_code 59278 2025-07-03T18:43:10.086674 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) d7d495756faa10b415d705cf26b69d2efbc5f190a834b6767386ff296629ea71
253 tests/services/test_file_service.py test_file_service.py source_code 5243 2025-06-21T00:01:46.460141 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 83e6e9a7584b7baeccbc84865d8c1d3e2a0223e7d2b9c066d3799c289196ab7d
254 tests/services/test_initialization.py test_initialization.py source_code 6939 2025-07-08T20:39:18.351946 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ebb8d11ff37dcb36c4844c68f873c8e0e8aa6ab1b82abb22ee96c4233e6ab287
255 tests/services/test_link_resolver.py test_link_resolver.py source_code 13618 2025-07-01T08:33:22.105766 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) c9fc56d0c3572770e95ca6175c213ed8b44c2a6ddddc298b5662cc2d8f41014e
256 tests/services/test_project_service.py test_project_service.py source_code 28152 2025-07-30T09:39:45.728920 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 430868ed796518937a3b0bed4d8c33be53264a6100d61d54b0745bb2b58bf29a
257 tests/services/test_project_service_operations.py test_project_service_operations.py source_code 4679 2025-06-21T00:01:46.442141 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) d4103b6c5fff7e74ba84b86033537b8522aa60fc75aec4974d52e1dfec68b1b4
258 tests/services/test_search_service.py test_search_service.py source_code 25299 2025-06-21T00:01:46.439133 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) b3e58258db2b727ab8a1792e52fcb038d3a0779bcd1b70f2d295a9d06e5e2513
259 tests/services/test_sync_status_service.py test_sync_status_service.py source_code 9926 2025-07-03T18:43:10.087266 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 00245ca356e33fc7afcd2d42cb5ec082038c95383d738067821cf1e8d23c9a4b
260 tests/sync/test_sync_service.py test_sync_service.py source_code 39659 2025-07-01T08:33:22.107088 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 0609aee9906549f27fc4289b5ac2d46a50e53e12a0615a2701f17fbe9a74e169
261 tests/sync/test_sync_wikilink_issue.py test_sync_wikilink_issue.py source_code 1959 2025-06-21T00:01:46.420370 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) db4b98dd7008a1a26b0fd02b9a2d03bca950b281efc19d98a91b017c500b23a5
262 tests/sync/test_tmp_files.py test_tmp_files.py source_code 5930 2025-06-21T00:01:46.424277 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) cead95a07b29f6c1ab933abe67f9648122f5f33d33753c8e4ab4934af96cbc25
263 tests/sync/test_watch_service.py test_watch_service.py source_code 14076 2025-06-26T09:28:51.860679 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) e324a4127f8bfe59798f3a08125488c701e88239b7482d87a0394b91c55b893e
264 tests/sync/test_watch_service_edge_cases.py test_watch_service_edge_cases.py source_code 2169 2025-06-21T00:01:46.409830 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) b87c5015330e9b551630aab7ef95ac1e7663f0ce46e912d8155b319e139559aa
265 tests/test_config.py test_config.py source_code 3680 2025-07-03T18:43:10.087558 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6a246431cff739274efda895dfa28f38710289a73e645312f5fa05fce0885ec8
266 tests/test_db_migration_deduplication.py test_db_migration_deduplication.py source_code 6166 2025-07-08T20:39:18.352690 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com) ab768e061531ca2edcd598c101b5d884e549f90c7a4b305a23713a199d263fbb
267 tests/utils/test_file_utils.py test_file_utils.py source_code 6199 2025-06-21T00:01:46.219673 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 4615bb20b92b8a79d9e086301f852b34c3360f17ecc209920d68528268ddeed4
268 tests/utils/test_parse_tags.py test_parse_tags.py source_code 1691 2025-06-21T00:01:46.223400 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) b83afdf18dac8b3411bd0fab1301683217d3d1abceaf2d22c09e79c709efd8eb
269 tests/utils/test_permalink_formatting.py test_permalink_formatting.py source_code 4193 2025-06-21T00:01:46.225738 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) d998a4ca846c78679728a918b6f71df67c30b6961ede3cffa609f2cf6ad160eb
270 tests/utils/test_utf8_handling.py test_utf8_handling.py source_code 5791 2025-06-21T00:01:46.228483 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 50ec792e572af51cd2d996d4ddacbcfd3bbd79aa928818a90c42117451ea829f
271 tests/utils/test_validate_project_path.py test_validate_project_path.py source_code 15346 2025-07-30T09:39:45.729224 jope-bm 1 jope-bm (joe@basicmemory.com) 27a4bf2a9d277e6cd773619e5241b20175d211ec60c284361bec404fd99143f3
272 uv.lock uv.lock other 232710 2025-07-30T09:39:36.619155 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) caa1e3f6e66464b884a78cab77df2fe303fa3c0dc573be0196474b6d3c150581
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,272 @@
path,name,category,size_bytes,modified_time,primary_author,contributor_count,contributors_list,sha256_hash
.claude/commands/release/beta.md,beta.md,documentation,3103,2025-07-01T08:33:22.037438,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),345f06e3f304470a8892e96979355919c8e5d8f4212ebbe543c3272fd15a5cbe
.claude/commands/release/changelog.md,changelog.md,documentation,4648,2025-07-03T18:43:10.075727,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),ea2484352f4f881aa71f546dcf84b0882d60c96673a88406aa2f7e12a282a1f4
.claude/commands/release/release-check.md,release-check.md,documentation,3344,2025-07-01T08:33:22.040060,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),7dd41a40e95c89153936c4ea60a3c989eccd5973fff9f6caef156de82f12bd04
.claude/commands/release/release.md,release.md,documentation,2925,2025-07-01T08:33:22.043674,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),0e4afd788fcf85fbe46094452e9b92a4290c3e23702283e72ee56aa9288476e5
.claude/commands/test-live.md,test-live.md,documentation,18720,2025-07-03T18:43:10.076403,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),21a9edeefb3e5673d833151a2fd0464087c0f0a9d056fdd7fca474c1d177d6fc
.dockerignore,.dockerignore,other,623,2025-07-01T08:33:22.048301,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),b3079bffad89d07186c9166725075524ffc07ceea6dc1d53fdbea6dca9585684
.github/ISSUE_TEMPLATE/bug_report.md,bug_report.md,documentation,967,2025-03-01T14:22:50.968994,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),0d34443ddda84a5e9726c8268083c9646d2cc64ff9c5ebf56c89587cef4c7706
.github/ISSUE_TEMPLATE/config.yml,config.yml,configuration,374,2025-03-01T14:23:26.936836,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),f4e21bb9b7f053509b6c08548a2649a5d62f44f0106810aa8c915d2f29acc623
.github/ISSUE_TEMPLATE/documentation.md,documentation.md,documentation,557,2025-03-01T14:23:40.020117,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),78f80a6b9a2b954695f74a847a068e0b780da6020572401c13c931555a4a9db5
.github/ISSUE_TEMPLATE/feature_request.md,feature_request.md,documentation,786,2025-03-01T14:23:06.344081,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),cee834de442221d53f3d2eeb85b81f3a641b3a7cb587ef8a169452305007439d
.github/dependabot.yml,dependabot.yml,configuration,529,2025-03-15T10:40:16.936258,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),8c6c2ef9a1417da63adb49c70f45524129bd5c46bbd7452b6b97cfe65205f89d
.github/workflows/claude.yml,claude.yml,legal,4628,2025-07-03T18:43:10.076958,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),d026f1756310781b85abc491b454cc31b52dfce4b951cdf8a4a555b6b047604c
.github/workflows/dev-release.yml,dev-release.yml,configuration,1405,2025-06-03T08:31:42.915367,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),58bcfc9e11e210e02dee76aabe7d1378da4edafb7f61857049ee6ccee9d45ffa
.github/workflows/docker.yml,docker.yml,configuration,1621,2025-07-01T08:33:22.049870,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c918a86f2b16ebb45182a73c45272bcd6cea7022cc6e0eaeb2b2a24d8eebfe4d
.github/workflows/pr-title.yml,pr-title.yml,configuration,887,2025-02-17T16:29:35.733646,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),b6df62a20235496feba4d9419c85d6e8446c4d447ad69e7f57b51cc8953f68f4
.github/workflows/release.yml,release.yml,configuration,2479,2025-07-03T18:43:10.077261,Drew Cain,2,Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co),163307a99e2f3d317af6ae8927e1537654d88bf574a6958ac0bcd2d4de266b2a
.github/workflows/test.yml,test.yml,configuration,1426,2025-07-01T08:33:22.054430,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),ab09084a11be8adfc83fc6285c652fbe44a4584aa7d7d3658fcf0c57112d1640
.gitignore,.gitignore,other,566,2025-07-01T08:33:22.055006,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),358658b0faa8c3145e539421deba4ad0bac4a8ea2e608b2c2f867a822c67b36f
.python-version,.python-version,other,5,2024-12-02T16:41:37.012830,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),7b55f8e67b5623c4bef3fa691288da9437d79d3aba156de48d481db32ac7d16d
CHANGELOG.md,CHANGELOG.md,documentation,53647,2025-07-06T10:34:45.550847,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); semantic-release (semantic-release),0b165eb18975ff253cde08f4687b7808ac6ce63b6d8c10912c495a76c9058d1a
CITATION.cff,CITATION.cff,other,302,2025-02-17T16:29:35.735146,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),bc4878cf6f80bb64655f3f93a5821ffe0924ad8311c784b5f81a36093fcffdda
CLA.md,CLA.md,legal,1342,2025-03-01T14:12:38.435132,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),c238c7ff4f2b8451c6bba1bbacd10b04fb7d41d9a9cd76a62644d8193791d169
CLAUDE.md,CLAUDE.md,legal,12310,2025-07-03T18:43:10.078147,Drew Cain,4,Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); Ikko Eltociear Ashimine (eltociear@gmail.com),751f10c1a9a9c0ba2054d9c9cd36cf0989d6a347491fd505bb6504ee74b12900
CODE_OF_CONDUCT.md,CODE_OF_CONDUCT.md,documentation,509,2025-02-17T16:29:35.735238,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),b276b464e85eb1e5d9f09d90bba9573508a7bf81d2eeb38d0f25bd9f2fa66198
CONTRIBUTING.md,CONTRIBUTING.md,documentation,7329,2025-07-30T09:39:36.607158,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co),ca822fe39cc529bfc3822d317ab8a20fd141ec3ecb8c20de9e2eca3edce1c282
Dockerfile,Dockerfile,other,856,2025-07-01T08:33:22.056908,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),c45501905423990e31ffdb8ae4e976eca6423c6784905c1036bcc7795b70feef
LICENSE,LICENSE,legal,34523,2024-12-02T16:41:20.739392,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef
README.md,README.md,documentation,16261,2025-07-03T18:43:10.078281,Drew Cain,6,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Jason Noble (github+jasonn@jasonnoble.org); Marc Baiza (43151891+mbaiza27@users.noreply.github.com); Matias Forbord (codeluggage@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),2c4b82b30f49ab465617034fda999157450ed168f8e097fe8e9ca7ac65a197a7
SECURITY.md,SECURITY.md,documentation,303,2025-03-12T16:04:01.989927,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),96932268b8fe737577941b49e9dff5ff8ad59c11cc3f9a016989589ef5c98c1f
docker-compose.yml,docker-compose.yml,configuration,2534,2025-07-01T08:33:22.059281,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),4a9fdfd2f0d42b148fe021eee77fd5e772c7ba9c2fe5d0cc310c8c7a64257417
docs/AI Assistant Guide.md,AI Assistant Guide.md,documentation,16423,2025-07-01T08:33:22.060491,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c717869a3d356852077026591364ecb0589a6d53f9764af027c57d142ea7ff55
docs/Docker.md,Docker.md,documentation,9088,2025-07-01T08:33:22.060976,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),e13d3f50148353046b52d9d493986880f76f2914ef4efe1a759b2cc9d10913a4
justfile,justfile,other,5496,2025-07-30T09:39:36.608008,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com),a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
llms-install.md,llms-install.md,documentation,2307,2025-06-29T14:19:28.826375,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),674105eb85c8f1d59fc2ddc6840f2a2a3e293358687a305c37dffc8144304f97
memory.json,memory.json,configuration,90075,2025-06-29T14:19:28.827172,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),22d5eee995ca0548b7d9dd152f9a3690c42a514cbf38fea750b3ed2a17dd99c5
pyproject.toml,pyproject.toml,configuration,3279,2025-07-30T09:39:36.608791,Paul Hernandez,4,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); semantic-release (semantic-release),428a5d64d4555dc7cd2a81b89a3444bdb1d2f3017257fbc9160bd0ae982718e1
smithery.yaml,smithery.yaml,configuration,433,2025-03-11T16:36:03.389716,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),63f13499cf4fee0751b5005f5bd85597dc1c7f9a881f4ec8147cc23e95e75c2d
src/basic_memory/__init__.py,__init__.py,source_code,256,2025-07-06T10:34:45.551291,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@gmail.com); semantic-release (semantic-release),c946490ab9d64957d25df37f721392024916f8a81ed838f70a8e938b1c2637db
src/basic_memory/alembic/alembic.ini,alembic.ini,configuration,3733,2025-02-23T17:31:23.198433,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),20466c9c5f026db66793006bebb2f328a3686c7bb34da40d52f33c3eca29e717
src/basic_memory/alembic/env.py,env.py,source_code,2838,2025-07-08T20:39:18.336650,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),e241d987eadfcd5011cbd9ddbecb833d306de87937759d81c08d37d04a6906b0
src/basic_memory/alembic/migrations.py,migrations.py,source_code,718,2025-06-21T00:01:59.613806,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),96b8873d70dd04b48d5c45b7413a54d1224ab9577557ef0dad5929377a9fb144
src/basic_memory/alembic/script.py.mako,script.py.mako,other,635,2025-02-17T16:29:35.739659,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),304a8bfb6a804e5493f5300e79882c70c9f5bb2e87512f4d16f0e097dddd323f
src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py,3dae7c7b1564_initial_schema.py,source_code,4363,2025-06-21T00:01:59.595762,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),9536d69409ddd5eb3bc54f7d0e82607da45ed7f2ad7bc4cbf7cae2a9e28657cd
src/basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py,502b60eaa905_remove_required_from_entity_permalink.py,source_code,1840,2025-06-21T00:01:59.604717,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),93ac584e660f33d468b3eec203b070641298c282bf82655d0beda7f050237681
src/basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py,5fe1ab1ccebe_add_projects_table.py,source_code,4379,2025-06-21T00:01:59.601299,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),d82098f5aca3cdbada60c7229ea4ac262f527349eed9ef9e864509bacfaccf7e
src/basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py,647e7a75e2cd_project_constraint_fix.py,source_code,4210,2025-07-01T08:33:22.063463,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),604ac5908a5976272f52297866d13abb14a99390590935d9fd37cf13e32049fa
src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py,b3c3938bacdb_relation_to_name_unique_index.py,source_code,1428,2025-06-21T00:01:59.598551,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),46c1b2990cdf4575752d234a8b28b494c8a54f15b5360c12f6347aef27b66a92
src/basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py,cc7172b46608_update_search_index_schema.py,source_code,3679,2025-06-21T00:01:59.610909,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),9036af47d42ac7dc12bb93f7a9de1266aff0688b031b550c4e0d92983a24b4c5
src/basic_memory/api/__init__.py,__init__.py,source_code,72,2025-06-21T00:01:59.579981,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),c02a63fb6d63d43d0acca97d425eae9cb055158d0ad6e1a9fc57926512adaa99
src/basic_memory/api/app.py,app.py,source_code,2897,2025-07-08T20:39:18.337081,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ad673ac2c09410af5830f6afdffaa28b0040480dacd52ff01750b8b2c066e8cf
src/basic_memory/api/routers/__init__.py,__init__.py,source_code,398,2025-06-21T00:01:59.549416,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),4443b908a43dea8e6fb4658009cb08c48a56c9b21449e29773cdd159b59cf014
src/basic_memory/api/routers/directory_router.py,directory_router.py,source_code,2054,2025-06-21T00:01:59.567371,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ac1407beec1f7d43a4d222af704b3642559c960bebf2eaf6e1ffe91f7fac5514
src/basic_memory/api/routers/importer_router.py,importer_router.py,source_code,4513,2025-06-21T00:01:59.561600,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),c45502a2b90f5a8f00174c9ad14adc2e65cd7fc0a33d9740a9d750207f0f3bea
src/basic_memory/api/routers/knowledge_router.py,knowledge_router.py,source_code,9738,2025-07-01T08:33:22.064119,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),e1d0ffb4fa5c246582ba844a11b417092de1a173567be55b7060bbc55fbc5681
src/basic_memory/api/routers/management_router.py,management_router.py,source_code,2730,2025-07-08T20:39:18.337438,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),cdbce294dcec6146c56c4dae15744cdf77039fd21b23eef7cbc0b5f9bfb5f4ee
src/basic_memory/api/routers/memory_router.py,memory_router.py,source_code,3003,2025-07-01T08:33:22.064510,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),6bd0a7c775e0c1290efb6001173237c0cdcfa0c1b1bb27c9145a7b35f1596a97
src/basic_memory/api/routers/project_router.py,project_router.py,source_code,8444,2025-07-30T09:39:45.721369,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),6539c69f1d506c65d14cc2c151876b19057f37d84d51a9e6f464ed47a4692648
src/basic_memory/api/routers/prompt_router.py,prompt_router.py,source_code,9948,2025-07-01T08:33:22.065221,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),e30c6aebe35112054933c37d0b462c3750005030fc9e44c23b35b2cd2a93485c
src/basic_memory/api/routers/resource_router.py,resource_router.py,source_code,8058,2025-06-21T00:01:59.570399,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),584244a84698ff24ca8f9f94fab5b891740a51c25f972920c08ebf83f478d5c9
src/basic_memory/api/routers/search_router.py,search_router.py,source_code,1226,2025-06-21T00:01:59.543020,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),183eb68e50904e22ff54db1d89b8be6f57fa1f8d0a096a3d497d8297b607e105
src/basic_memory/api/routers/utils.py,utils.py,source_code,5159,2025-07-03T18:43:10.079143,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),9e60f57da24e1dc9d64236c26a7a2353003dc618a77fbeb853c494aa3357a57c
src/basic_memory/api/template_loader.py,template_loader.py,source_code,8607,2025-06-21T00:01:59.576132,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),7b16d3797c89460c8b1664a335eae8c634fb5f60d6166d97e6a5156b776b6d83
src/basic_memory/cli/__init__.py,__init__.py,source_code,33,2025-06-21T00:01:59.456735,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),6ab70a2c05910e13c3ef1e6df34325be265e633c076741998a0cb2dcd295a069
src/basic_memory/cli/app.py,app.py,source_code,2268,2025-07-08T20:39:18.337708,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),f710670380d707c4458ad881f7a25bb3c5cdb2bcff59f139f37421264fe8f2f8
src/basic_memory/cli/commands/__init__.py,__init__.py,source_code,393,2025-07-08T20:39:18.338070,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),de8a23702f98fb844fa9f7fdbedc16ce24ff4f8baf05589c2f4a521cd8a55645
src/basic_memory/cli/commands/db.py,db.py,source_code,1480,2025-07-08T20:39:18.338326,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),704a1096d80ab9d12e247d029fe6223e935a0d0ceee7e615c020f46a720828e0
src/basic_memory/cli/commands/import_chatgpt.py,import_chatgpt.py,source_code,2856,2025-07-08T20:39:18.338787,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),8957cca3acab6351335624a518bdc19d956487e93d86dd1b6c2309e9d5850ae5
src/basic_memory/cli/commands/import_claude_conversations.py,import_claude_conversations.py,legal,2946,2025-07-08T20:39:18.339053,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7bc97838732bf00f4fb4a80e3ba4fdfbce8971ae85cc2ac902c3b3a3e110ae57
src/basic_memory/cli/commands/import_claude_projects.py,import_claude_projects.py,legal,2891,2025-07-08T20:39:18.339310,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),6321577075801c99ad47a0cdc13b5aa3c9ca102a05ca8f06ae2a51125a8c43bc
src/basic_memory/cli/commands/import_memory_json.py,import_memory_json.py,source_code,3013,2025-07-30T09:39:45.721637,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7ea193f6720b2672290ad16c79a143a31d88a1d567838f12b9ebf9110a7c0338
src/basic_memory/cli/commands/mcp.py,mcp.py,source_code,2593,2025-07-08T20:39:18.340025,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),3e1be3d27b18ef4743204b7cf8a37fbc2a1a91fc1636c468e08cefcd8597bac6
src/basic_memory/cli/commands/project.py,project.py,source_code,13836,2025-07-30T09:39:45.721838,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),af3e6f30410ab48355f4b1a46aaa3aa5423cb0a0490f51447db1d25e09f39916
src/basic_memory/cli/commands/status.py,status.py,source_code,5809,2025-07-08T20:39:18.340324,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),b6d0ea237d714d054f69b24a2058beba74e224a2fa4a80a525d62a2aa2ea2d70
src/basic_memory/cli/commands/sync.py,sync.py,source_code,8213,2025-07-08T20:39:18.340627,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),703b3e73c4c33850c27f6f4c07abbf5d4ce5dc43f706bf0c43264bbb45f8b213
src/basic_memory/cli/commands/tool.py,tool.py,source_code,9540,2025-07-01T08:33:22.070903,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),9b2fa400b9f7921bf55b602f8bbdfa36b1ec7e4a5bc8fe7b9838b92e31f0a9ed
src/basic_memory/cli/main.py,main.py,source_code,465,2025-07-08T20:39:18.340868,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7a405b12bff98e319968526238ae15859df0362bba5a3857562fecca47b5c70d
src/basic_memory/config.py,config.py,source_code,12487,2025-07-30T09:39:45.722031,jope-bm,5,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
src/basic_memory/db.py,db.py,source_code,7428,2025-07-08T20:39:18.341550,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),25e053b0b3f9e8fa823b05fc099001efe8a3a4f6e0f80728e5079ba45aa406d8
src/basic_memory/deps.py,deps.py,source_code,12144,2025-07-08T20:39:18.341870,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),bce28cd9ca549b8afce0bc689cc1fb2dc80cd5b929f85114aa682bb8ae82411d
src/basic_memory/file_utils.py,file_utils.py,source_code,6700,2025-06-21T00:01:59.587200,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),79ac5328b2c46d3232fcc6ff22ffc39ade085c0252ad91958be2a7ae9c9c8b71
src/basic_memory/importers/__init__.py,__init__.py,source_code,795,2025-06-21T00:01:59.295471,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),0537015bdecfded85cb166b9c3db50b2f3aef329c70e0c36fbcb4f824099a21f
src/basic_memory/importers/base.py,base.py,source_code,2531,2025-06-21T00:01:59.305335,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),6b0c1efd4f827f348834aa0cea2ae8eebbb842a2e5b1f5f3747ced0ef79b9f13
src/basic_memory/importers/chatgpt_importer.py,chatgpt_importer.py,source_code,7372,2025-06-21T00:01:59.308956,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),dfa540b196ab23b5c4e6bfcac4da567873351cf7c78fa35f4c3b87e84c58e218
src/basic_memory/importers/claude_conversations_importer.py,claude_conversations_importer.py,legal,5725,2025-06-21T00:01:59.288540,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),a7bc9e872f4f81ce5f79fe9dd1a6fd0f00a6f020a2cb2664184a97f14eeb1dbc
src/basic_memory/importers/claude_projects_importer.py,claude_projects_importer.py,legal,5389,2025-06-21T00:01:59.291737,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),a452675fd9bb18ebf64eb4bd7f69ccd7e993b617931015a3c4ab700d67493863
src/basic_memory/importers/memory_json_importer.py,memory_json_importer.py,source_code,4631,2025-07-30T09:39:45.722316,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),bc7d045299f17ed9ad5cebff7b1423250ec28a11130e47a0ac48d3ab82bdeafc
src/basic_memory/importers/utils.py,utils.py,source_code,1792,2025-06-21T00:01:59.298428,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),ee7e22fd4812362394f62ed8944abe80738c1e3ab88131faf639f4a8ccfd6c89
src/basic_memory/markdown/__init__.py,__init__.py,source_code,501,2025-06-21T00:01:59.316337,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),0ddce2a025ad0e729aab4e411d82e02ffefc15ac041cba4b5e7a7e90f4957c87
src/basic_memory/markdown/entity_parser.py,entity_parser.py,source_code,4516,2025-06-21T00:01:59.321461,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),1b0d1177358645ccbadd170b3f62760dc9bd838d38c741978ab0da5836234d68
src/basic_memory/markdown/markdown_processor.py,markdown_processor.py,source_code,4968,2025-06-21T00:01:59.325241,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),4b99f2ebdceed9d96a3bbb562682eba4b4b383c7a64080eaec3bbba25a4952c9
src/basic_memory/markdown/plugins.py,plugins.py,source_code,6639,2025-06-21T00:01:59.312733,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),82d2332918e866ccaf06a2e954d9eb9b397f71b4d9e591a7f2472e5f1423464a
src/basic_memory/markdown/schemas.py,schemas.py,source_code,1896,2025-06-21T00:01:59.329982,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),7b2c580abd615725a62267257b46ac13922dfc30fa01192a681658bb528ae67e
src/basic_memory/markdown/utils.py,utils.py,source_code,3410,2025-07-03T18:43:10.079889,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),1b757f0d06e60e3ea276c0b2ea44fe1a156a895e093c1e607d6286e702bf4ae4
src/basic_memory/mcp/__init__.py,__init__.py,source_code,35,2025-06-21T00:01:59.404757,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),76c0ce84aaa361e21b0942db1c8c5f708b536eab9d12e120d4da7ce9eab41844
src/basic_memory/mcp/async_client.py,async_client.py,source_code,925,2025-07-30T09:39:45.722547,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),6531f4387f30a30a322197ff51d6eacae4b25a58c648095931b3d1145d1da303
src/basic_memory/mcp/project_session.py,project_session.py,source_code,4203,2025-07-08T20:39:18.342906,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),4da403ec93dec9063eeb8b05246e3501740d162cc77a067d3ce54ba43d690819
src/basic_memory/mcp/prompts/__init__.py,__init__.py,source_code,615,2025-07-08T20:39:18.343263,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),f8197d0e08f64c3f4f50b8f38203ea5eebcf123582472ed2f58834de1dbe53b0
src/basic_memory/mcp/prompts/ai_assistant_guide.py,ai_assistant_guide.py,source_code,821,2025-06-21T00:13:53.262832,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),f13239c4e6e2455730bfac3d6f2d71407957d3086fc84efe2c6b22a833114c58
src/basic_memory/mcp/prompts/continue_conversation.py,continue_conversation.py,source_code,1998,2025-06-21T00:13:53.263309,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),aec9a50b657b7bb1ba0c02b42bcdb9bc5b0f2a0b1143bd361c5ce7ea59076833
src/basic_memory/mcp/prompts/recent_activity.py,recent_activity.py,source_code,3311,2025-07-01T08:33:22.073986,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),d2fd5cddbd927430f15d5b85f1e3a3368a18cb4e2e4587a5d29749d2b9e0830e
src/basic_memory/mcp/prompts/search.py,search.py,source_code,1692,2025-06-21T00:13:53.264093,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),9dbf3c319cbdb5d5bf32608b51522d8ae92b2dd6f7c441d62efd0954b525738a
src/basic_memory/mcp/prompts/utils.py,utils.py,source_code,5431,2025-06-21T00:01:59.425042,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),55a72b6eac18b724a9225608aca1e8e6cea3b684ccb1c609aab1511f7ce90ba4
src/basic_memory/mcp/resources/ai_assistant_guide.md,ai_assistant_guide.md,documentation,14777,2025-03-28T19:12:38.653627,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),aa76160e46256fe2662ae3a86799659916acfede1d5835c1fe2f0b136efb2e0b
src/basic_memory/mcp/resources/project_info.py,project_info.py,source_code,1906,2025-06-21T00:13:53.264918,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),2dc5244f1e225c17d4e8ba784d5721efc3aa2e8a5b3b2e258f22a77ebe155eca
src/basic_memory/mcp/server.py,server.py,source_code,1248,2025-07-08T20:39:18.343419,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),0a910cae27d61ea923e90fbe2d4b5c7cacf3b29d3e156f0b7f77872ab973c761
src/basic_memory/mcp/tools/__init__.py,__init__.py,source_code,1619,2025-07-03T18:43:10.080029,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co),872b771dd52ec3b763645a2baf6e10a70f049ce3106830a6d3f053b3e09a5fe6
src/basic_memory/mcp/tools/build_context.py,build_context.py,source_code,4611,2025-07-03T18:43:10.080426,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),724280b77b8f5f3e61cd3fdeebc3ee66e2bcfedaaea3638e6a2e2e33fcb1978e
src/basic_memory/mcp/tools/canvas.py,canvas.py,source_code,3738,2025-07-01T08:33:22.076204,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),db617d1bd81f3dbfa5f22d41e6b6b825afe1f73618f37acf63d9830390b98246
src/basic_memory/mcp/tools/delete_note.py,delete_note.py,source_code,7346,2025-07-01T08:33:22.076726,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),b52c9173f5600662f255e7a70a5c17d5293ef8b29c19a8403334d7da62b65c8b
src/basic_memory/mcp/tools/edit_note.py,edit_note.py,source_code,13570,2025-07-01T08:33:22.077181,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),ab8c7e7fbfa3fe5fb0ce6d7bf805454f5fd6182a340aae25237b1e6127b6d5a6
src/basic_memory/mcp/tools/list_directory.py,list_directory.py,source_code,5236,2025-06-21T00:13:53.267453,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),f85c43b02aeee580f4d8ce2a9100c0bab1095b245a0bb608e1847ace0d1ab51f
src/basic_memory/mcp/tools/move_note.py,move_note.py,source_code,17541,2025-07-30T09:39:45.722919,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),5ef27d9baaebc2c5656fc49c4798a4e4e40a70b6cd9d1e761dbcae021bad558c
src/basic_memory/mcp/tools/project_management.py,project_management.py,source_code,12944,2025-07-06T10:34:45.552403,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co),cdac736d65129f689a85c6b82f8e043bc84a31657ead7a8331771171eeaa860f
src/basic_memory/mcp/tools/read_content.py,read_content.py,source_code,9125,2025-07-30T09:39:45.723469,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),96a13addac507fd874394dfa2e1e5ed71af5eb89d0872c1d68cacf9cac3f8724
src/basic_memory/mcp/tools/read_note.py,read_note.py,source_code,8132,2025-07-30T09:39:45.723828,jope-bm,5,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),dff53ce02b16d9c5fca315b3f09bb70867a89d614b0ef3f0548dce87978048ee
src/basic_memory/mcp/tools/recent_activity.py,recent_activity.py,source_code,4833,2025-06-21T00:13:53.269120,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),5d58cd2402679b1bf3c7dfcbb3503e40e776c9347ba499524d3b91c528af98de
src/basic_memory/mcp/tools/search.py,search.py,source_code,15883,2025-07-03T18:43:10.081557,Drew Cain,4,Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),8519b0057468c4450eb543a2f561bcd0d7cb96e1ce1b95e948e02b309ba6b7ca
src/basic_memory/mcp/tools/sync_status.py,sync_status.py,source_code,10517,2025-07-08T20:39:18.344231,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),73dd73b186eaec8b82e6b17e39a67c27082d24e0653dc4e4b35712bd83b80a22
src/basic_memory/mcp/tools/utils.py,utils.py,source_code,20892,2025-07-30T09:39:36.616253,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),a95004911e276822eba88a3fef15c5b9ba861b1ca9a2ecfe0c1e3fb2f4ef0116
src/basic_memory/mcp/tools/view_note.py,view_note.py,source_code,2499,2025-07-01T08:33:22.080024,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),75d357c72113b1d0394987e521a4158ff09b77b23b08b56cddab5144331b1a68
src/basic_memory/mcp/tools/write_note.py,write_note.py,source_code,6612,2025-07-30T09:39:45.723985,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),771396fb49d397feb5f7e36321d4d233c22c4d1b53414912c849957c6e96fc83
src/basic_memory/models/__init__.py,__init__.py,source_code,333,2025-06-21T00:01:59.437212,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),8f40b876d162f85384690291f1d416106f9d26d750d793414e2260e276c85cd5
src/basic_memory/models/base.py,base.py,source_code,225,2025-06-21T00:01:59.447249,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),e2101727c084d519e32a16f6de53ddf9033b1bf15721d4e8c0c27d6d18b1b295
src/basic_memory/models/knowledge.py,knowledge.py,source_code,7087,2025-06-21T00:01:59.444376,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),005c5f292f1f45ae372aadc48c9080b9fa6d7b854d0bb7ecf587ec843ac1e28d
src/basic_memory/models/project.py,project.py,source_code,2773,2025-07-01T08:33:22.080907,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),a14ad06943aeeff9ae4a5fa2dfc0e1d07ce6085acc02dc20c402c3513b7d9397
src/basic_memory/models/search.py,search.py,source_code,1300,2025-06-21T00:01:59.440923,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),3e143cc38b5a0294af8e1d43a4f841e1c1fd193b76136a68f83159ce19e86646
src/basic_memory/repository/__init__.py,__init__.py,source_code,327,2025-06-21T00:01:59.256363,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),3162bea3c42292accea5ee52c8f6ca4368a8079056034529cfae6d820f84d035
src/basic_memory/repository/entity_repository.py,entity_repository.py,source_code,10405,2025-07-03T18:43:10.082570,Drew Cain,3,Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),e2a8d1eba6c8d64bc61d7168df0fe8c29a670858bf9daeac464a0f440273fae0
src/basic_memory/repository/observation_repository.py,observation_repository.py,source_code,2943,2025-06-21T00:01:59.269646,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),aa132f1cb4a36a84f715afdc40e2ac4f98d83e3eba1974b2b4404cc0b020ca04
src/basic_memory/repository/project_info_repository.py,project_info_repository.py,source_code,338,2025-06-21T00:01:59.252582,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),f172d50182a405643a19b2a3d62a80f4e2b41463c775394eb3b66d7de51ff6df
src/basic_memory/repository/project_repository.py,project_repository.py,source_code,3803,2025-07-30T09:39:45.724344,jope-bm,2,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),9083ba214b2bcaaf98164d9c33cda7add52bc77f7269ecb6e43cdd5088728b7c
src/basic_memory/repository/relation_repository.py,relation_repository.py,source_code,3179,2025-06-21T00:01:59.246745,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),cfb3a8e59cff27e063e91bd00e949647bdd92e4d9fc46edeee3acc6de15e26f4
src/basic_memory/repository/repository.py,repository.py,source_code,15224,2025-06-21T00:01:59.273355,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),3096fe71bf106506cbf86aeafe2aafe0aabbe5a5f6c90a212c8aa1e53e1f171c
src/basic_memory/repository/search_repository.py,search_repository.py,source_code,21936,2025-07-03T18:43:10.082844,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),a972f73d1b71dac5770e8eb3793c6c9ac44e4e792f9dab528f8c4e6c6a80bcaa
src/basic_memory/schemas/__init__.py,__init__.py,source_code,1674,2025-06-21T00:01:59.514555,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),98480815c75379bfafe32d2090e87fa40e73caa19b664fbd5db5ea9528ba58cb
src/basic_memory/schemas/base.py,base.py,source_code,6738,2025-07-01T08:33:22.082193,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),171f7b0c4ab33abef2f7379eb1e3bda950586ce7edff83907fd12255f84e267e
src/basic_memory/schemas/delete.py,delete.py,source_code,1180,2025-06-21T00:01:59.502563,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),50047624af7d58c8f780ffb2a065a51c3dde64492f9534917fc42860813e59fc
src/basic_memory/schemas/directory.py,directory.py,source_code,881,2025-06-21T00:01:59.529023,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),17dfcbac9a91a9bfe43b4f060ca2735cb6f69e16d81b63dd554a397830ff29fe
src/basic_memory/schemas/importer.py,importer.py,source_code,693,2025-07-30T09:39:45.724633,jope-bm,2,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ac33df423ca32b28ce7b6ea9c29d541f8783a86c0c29f78db35163bf93f139cd
src/basic_memory/schemas/memory.py,memory.py,source_code,5833,2025-07-03T18:43:10.083167,Drew Cain,3,Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),acb4a953a553feca672c4895798a7d948ec51f922f75b66dd7d55716e3bebed3
src/basic_memory/schemas/project_info.py,project_info.py,source_code,7047,2025-07-01T08:33:22.083248,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),7dc3635297b6e7fe6e3262b2d78da26f7a79a846a4cecd5624f2e4828979cf2c
src/basic_memory/schemas/prompt.py,prompt.py,source_code,3648,2025-06-21T00:01:59.525753,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),4a92157d9a6b413f04e6e3f8d23dc2a417369c729f9703a8de2643ec114f2071
src/basic_memory/schemas/request.py,request.py,source_code,3760,2025-06-21T00:01:59.511789,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),32fe44beb2d99452223ebf1d3a3a3fe105ef92c79885023b71dfd7db30ecc503
src/basic_memory/schemas/response.py,response.py,source_code,6450,2025-07-03T18:43:10.083478,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),5eea4660a2abe48d83ed083d1c2483fdcfb403e0b504f03c14d22f1cae866abb
src/basic_memory/schemas/search.py,search.py,source_code,3657,2025-06-21T00:01:59.522804,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),cb032c0c60102b6b0ed934f995cf9d6bf93aece296d71d537a75e8ae61c75afe
src/basic_memory/services/__init__.py,__init__.py,source_code,259,2025-06-21T00:01:59.655059,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),5c6b7c597ddf5ffd0af4bdfb32ccbc1c5f2794c658206dee43a9945fafe226d8
src/basic_memory/services/context_service.py,context_service.py,source_code,14462,2025-06-21T00:01:59.672086,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (paulmh@gmail.com),e1178b005e6a89f03d6b239e3c6b152aeb30d535a3f280734440a3aec16228f2
src/basic_memory/services/directory_service.py,directory_service.py,source_code,6017,2025-06-21T00:01:59.648501,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),fd838f5ec79033892777b3c8140868f4b57f13e1658d513959524a434b9f959b
src/basic_memory/services/entity_service.py,entity_service.py,source_code,30440,2025-07-03T18:43:10.083956,Paul Hernandez,4,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),7cd5163eca6b8a0772e838c8c869e478166763cd6a2d745b0b9aa5c25a65baee
src/basic_memory/services/exceptions.py,exceptions.py,source_code,390,2025-06-21T00:01:59.668908,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),a158d0af9d1742a9c5ab5f8c34a062948d9286d1c3c72a5abf20e4d547b21e1c
src/basic_memory/services/file_service.py,file_service.py,source_code,10059,2025-06-21T00:01:59.641785,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),8c2ae69c4913438b7d1c5ecbfcce812fbb5d0ea8e3cedcbd8694e8f40cf086f3
src/basic_memory/services/initialization.py,initialization.py,source_code,6715,2025-07-08T20:39:18.344482,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),4dde58b799cf4ac90631e59949b55b3355a93bdf99df0d9c2408c0cea67e10c4
src/basic_memory/services/link_resolver.py,link_resolver.py,source_code,4594,2025-07-01T08:33:22.084908,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),d7efd516cbea753e6b5411def09aeaeb7539f5743487119ecf163c736e138a8c
src/basic_memory/services/project_service.py,project_service.py,source_code,30125,2025-07-30T09:39:45.725079,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),bce95c00aa9a1da0a8c8090ee34b5b1494207f8c99e6c25de4ec43fddc38f6be
src/basic_memory/services/search_service.py,search_service.py,source_code,12782,2025-06-21T00:01:59.665439,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),7392b2d2e7f3ed83c5807855ccd450e0e79c17f254aedee700bce9323a1b5b83
src/basic_memory/services/service.py,service.py,source_code,336,2025-06-21T00:01:59.636167,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),57e77ff20395d3bcc62100e92fe2acaacdd937d977a9fdc764e2b57ff60d4da8
src/basic_memory/services/sync_status_service.py,sync_status_service.py,source_code,7504,2025-07-03T18:43:10.084229,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),0a025d689e8e16f1632872104952105fc904537f3d32bcff592eb1f1dc76fbb7
src/basic_memory/sync/__init__.py,__init__.py,source_code,156,2025-06-21T00:01:59.621137,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),0951e0b981f8e7b876bb6c6833c2af3a29490bbd5726567ea9487c947723623e
src/basic_memory/sync/background_sync.py,background_sync.py,source_code,726,2025-07-08T20:39:18.345062,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),549af64ae91128b76c6df07ef517b82de85ca4ad796be44b5c0141fac01d4513
src/basic_memory/sync/sync_service.py,sync_service.py,source_code,23387,2025-07-01T08:33:22.086507,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),0310b927561370f593980d07773bce641b618b8fbf2d9e3890d174290a0344fc
src/basic_memory/sync/watch_service.py,watch_service.py,source_code,15316,2025-06-26T09:28:51.859209,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),240ba6ac7523575945f4db442b7da3820d26c9605f2d7a2d357c4e35e215f523
src/basic_memory/templates/prompts/continue_conversation.hbs,continue_conversation.hbs,templates,3076,2025-07-01T08:33:22.086792,Drew Cain,2,Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),b6bac31d25c0e52d0909b2273285092f4e31bc219107f92ed511cd407b65e992
src/basic_memory/templates/prompts/search.hbs,search.hbs,templates,3098,2025-05-25T10:07:54.520119,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),1f570222c1caa78542d46ac7d8a79407ca467b9bd714fa9bd953e8b72a667820
src/basic_memory/utils.py,utils.py,source_code,8654,2025-07-30T09:39:45.725428,jope-bm,5,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); andyxinweiminicloud (andyxinweimin263@icloud.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
test-int/conftest.py,conftest.py,source_code,8203,2025-07-08T20:39:18.345735,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),7231e1bc76cddc7c94433e853825dc0165b103bedf996c2c24eda6ae32fff5ad
test-int/mcp/test_build_context_validation.py,test_build_context_validation.py,source_code,6469,2025-07-08T20:39:18.346021,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),ee61d6b15cee0194fd74c1bcf9361d5a839f6081c7d571ef081da8fcefe1bbf2
test-int/mcp/test_delete_note_integration.py,test_delete_note_integration.py,source_code,13175,2025-07-08T20:39:18.346366,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),497495249b8bcf0af177d8705c6899875460707697980434e560cfe56f86feb3
test-int/mcp/test_edit_note_integration.py,test_edit_note_integration.py,source_code,19822,2025-07-08T20:39:18.346730,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),9ba704badd05808aee0b5a686b7e250e1c865229ec92dab561cba5287b6f6fc6
test-int/mcp/test_list_directory_integration.py,test_list_directory_integration.py,source_code,14744,2025-07-08T20:39:18.347106,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),2c4336a59de63f17ed252bb52d1330ebca901ad83bfebc9e5aa67f132a3c77c6
test-int/mcp/test_move_note_integration.py,test_move_note_integration.py,source_code,19825,2025-07-08T20:39:18.347344,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),cbb17688d0f9551306a7520b059ace7f7dfba70fc20bf0ec98b1148000d7ad7d
test-int/mcp/test_project_management_integration.py,test_project_management_integration.py,source_code,33175,2025-07-08T20:39:18.347658,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),93a43cd699453a7602a5def839740ba5027ab914eba3e7b66a026860ed6bd567
test-int/mcp/test_project_state_sync_integration.py,test_project_state_sync_integration.py,source_code,7728,2025-07-08T20:39:18.347907,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),80b0f5b5cd359498dcbd3af7dcb6194afa4582d8efb3faa38d28675ee45e0748
test-int/mcp/test_read_content_integration.py,test_read_content_integration.py,source_code,11187,2025-07-08T20:39:18.348233,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),6955dd343b40df088ac79175e52723501d83796335a16e43b7f58e16e757e1b8
test-int/mcp/test_read_note_integration.py,test_read_note_integration.py,source_code,1381,2025-07-08T20:39:18.348463,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),b93050da68d0e9d47d46879a386567f688399e28390f4eb31ee37353dc27514b
test-int/mcp/test_search_integration.py,test_search_integration.py,source_code,14690,2025-07-08T20:39:18.348959,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),e0957ada9dd9b12eae5843a37d241ec9293ca929f18281e31197dae36bff589d
test-int/mcp/test_write_note_integration.py,test_write_note_integration.py,source_code,9472,2025-07-08T20:39:18.349398,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),b2b56fee8351168d0c65640c295d969dc4575d9bea87927b2dc07e591cabdff3
tests/Non-MarkdownFileSupport.pdf,Non-MarkdownFileSupport.pdf,other,106751,2025-02-23T17:31:23.206428,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),4aa96f9d0ee830ef6a1ec92243f302bbe2fa8971b84945f4eb5e276bf1ffde96
tests/Screenshot.png,Screenshot.png,other,190076,2025-02-23T17:31:23.207771,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),dd82612fe013c39810d28ae0f2bbcca6ab2c9925b035d7720bb02493ddf5d0a9
tests/__init__.py,__init__.py,source_code,141,2025-06-21T00:01:46.216456,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),2ac5605e69ad8829e96b3689632536539f1cdd3149a29c1903f25af0793a5d65
tests/api/conftest.py,conftest.py,source_code,1404,2025-06-21T00:01:46.359728,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),ea46cde47a37c1f43fe079e48f5d0de8007155a0187f4445abc4f2ffde9c1791
tests/api/test_async_client.py,test_async_client.py,source_code,1382,2025-07-30T09:39:45.725666,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),a2f96226a75e5cff2326ca48ddccea4915964d83e529f7b99958bc233ccdc31a
tests/api/test_continue_conversation_template.py,test_continue_conversation_template.py,source_code,5152,2025-06-21T00:01:46.390901,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),cca08b0291db0ad693c60d8dc203660021feab46d9eb530be7f67e0ebb52c1d3
tests/api/test_directory_router.py,test_directory_router.py,source_code,9376,2025-06-21T00:01:46.356659,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),428626589f0010512481ba495368b0bf2b9617b22764e588eaa3562e86da4d37
tests/api/test_importer_router.py,test_importer_router.py,source_code,16578,2025-06-21T00:01:46.368070,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),03edf6e7404c7350afe76e54ea2bf0d66f668bcdb49f59702067a67b3694309d
tests/api/test_knowledge_router.py,test_knowledge_router.py,source_code,45771,2025-07-01T08:33:22.090490,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),43a5f91ae1fb9038e65c09701336fbdc4dbf40c8319c94727e6312fda9d6d56e
tests/api/test_management_router.py,test_management_router.py,source_code,6220,2025-06-21T00:01:46.402764,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),f6a3f41f263f10c08281525571e2e39fc9ae818ebb7e9895e3f3f3115ea05677
tests/api/test_memory_router.py,test_memory_router.py,source_code,5555,2025-06-21T00:01:46.399667,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),2b8782015c893081fc67b7f13a7255d4e3dc45b27705a524f94918a339e2d486
tests/api/test_project_router.py,test_project_router.py,source_code,13288,2025-07-30T09:39:45.725847,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),af85d0870069d3831a9214372853994e5d12dc910e37298eb9958f72f6a8bb31
tests/api/test_project_router_operations.py,test_project_router_operations.py,source_code,1812,2025-06-21T00:01:46.381705,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),afd40879de3d7c508cca0feeae3cb7a8f3afcfbb06f59a6104189b1e3291376a
tests/api/test_prompt_router.py,test_prompt_router.py,source_code,5046,2025-06-21T00:01:46.396588,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),9115995b7dd8b44d8a9c06c5e1bbb177618286675a769889e35ac16f26fd9897
tests/api/test_resource_router.py,test_resource_router.py,source_code,13960,2025-06-21T00:01:46.384919,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ad3cb2cabaeab82a7e64253ceede7c1791e2bd70c92f60bc5f3be939292ca22d
tests/api/test_search_router.py,test_search_router.py,source_code,6409,2025-06-21T00:01:46.375427,bm-claudeai (AI Assistant),3,bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (phernandez@basicmachines.co),ba3e3809795e72db9dd9c8c729048bdbbbdb897c2a5aa8b175112b5d4b56d152
tests/api/test_search_template.py,test_search_template.py,source_code,5258,2025-06-21T00:01:46.378721,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),8f0d16a1de947d0466d96afed02e695f7648adac70b9ba99753250068207a686
tests/api/test_template_loader.py,test_template_loader.py,source_code,8035,2025-06-21T00:01:46.406694,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),4fd7c6c4862cc0d877d29244ecdc0b52a0693fc4574a9667663bee36bfd36b62
tests/api/test_template_loader_helpers.py,test_template_loader_helpers.py,source_code,7445,2025-06-21T00:01:46.372114,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),8dd35f11c9b3f2d96ac36263edf4430308728c04c9da543b1cb845b96ab20aa7
tests/cli/conftest.py,conftest.py,source_code,1185,2025-07-01T08:33:22.090848,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c3c73da7ad73224931e1fba4ebd70792221be607bfb1488bb5c9420bba03bdb8
tests/cli/test_cli_tools.py,test_cli_tools.py,source_code,13528,2025-06-21T00:01:46.318964,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),befb8cab50132e26a1b13b82ce8e93a3258282cf157a4230214bfcefeae65102
tests/cli/test_import_chatgpt.py,test_import_chatgpt.py,source_code,6719,2025-07-08T20:39:18.350234,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),df447335545248e2fb2417af33f8cc2f0143bd413a1783e41f89605de305b7c8
tests/cli/test_import_claude_conversations.py,test_import_claude_conversations.py,legal,5028,2025-07-08T20:39:18.350607,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ac4b3b9dfc331fa7fa32a2941c027ff8b6585a94a5ccfaaf0a4affe5cf19c00c
tests/cli/test_import_claude_projects.py,test_import_claude_projects.py,legal,4585,2025-07-08T20:39:18.350885,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),7eaab3d491d50ca59c8ba015ecd5740a5feae94d970ee17559c6df9d8558c2cc
tests/cli/test_import_memory_json.py,test_import_memory_json.py,source_code,4891,2025-07-30T09:39:45.726290,jope-bm,3,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com),d6734ce059dacf97bc764d848b2e2a6ba52fa96871bc331ec9d2ddc60b766bb6
tests/cli/test_project_commands.py,test_project_commands.py,source_code,6578,2025-07-30T09:39:45.726703,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),51e2ac7ff44b0d1a1db6da406c5c2e6aebceb1b63a1e8d5ab51bbaa834507157
tests/cli/test_project_info.py,test_project_info.py,source_code,3874,2025-07-01T08:33:22.091600,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),0b08950e95a5f276f14c56f6fe99af4d41b8f04548e9b7746f807c5ac4155578
tests/cli/test_status.py,test_status.py,source_code,4160,2025-07-01T08:33:22.091978,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),a1ff89fdac4bf57ba03be92ac79b8ca3210af0b1132d5a2bd0fc0c253fcbf988
tests/cli/test_sync.py,test_sync.py,source_code,3864,2025-07-08T20:43:47.816788,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),77e0f6b42b584a5acfda3c2ff194ff5e71ca57a40cb4484d563f5482bbbb4c0f
tests/cli/test_version.py,test_version.py,source_code,289,2025-06-21T00:01:46.305368,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),db95665178a32d391bd83011d073db269cefba9da8a4dc4d3e392d47bc0e0a8f
tests/conftest.py,conftest.py,source_code,15286,2025-07-08T20:39:18.351368,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),c6f38f103ce58144ffe4562f0c47dad849974e96327f02805e854447e19bc411
tests/importers/test_importer_base.py,test_importer_base.py,source_code,4329,2025-07-08T20:39:00.549594,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),da872a073f2b90fda3c98c9f44cf45884f4f682d91ec1b1f931d42c34eee1939
tests/importers/test_importer_utils.py,test_importer_utils.py,source_code,1827,2025-06-21T00:01:46.184343,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),cbee630c855ceb1de764ac0cdb1d5d781c16cd3a934d67166adc235c98f363d7
tests/markdown/__init__.py,__init__.py,source_code,0,2025-06-21T00:01:46.196570,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
tests/markdown/test_entity_parser.py,test_entity_parser.py,source_code,8669,2025-06-21T00:01:46.206579,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),496fdab6fe164634f8223f827585a4c0dedd30ccea7b0b69dc79004ad0996de2
tests/markdown/test_markdown_plugins.py,test_markdown_plugins.py,source_code,5139,2025-06-21T00:01:46.200759,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),9624d73462aadcd47fb0edd0d87880959a8982eecfa39529f21e3b292c34cb83
tests/markdown/test_markdown_processor.py,test_markdown_processor.py,source_code,5728,2025-06-21T00:01:46.194175,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),35042aa57dd2aeed848c624aa7f69decfb18e83c3b217eca1fdacfc4a10831db
tests/markdown/test_observation_edge_cases.py,test_observation_edge_cases.py,source_code,4295,2025-06-21T00:01:46.209598,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),6f1ab7684bfa8b02af0e0c42b3d7aed91fcd0b1c6288cbc9b6d6943c166979fa
tests/markdown/test_parser_edge_cases.py,test_parser_edge_cases.py,source_code,5142,2025-06-21T00:01:46.213291,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),d511d7d1187e3c1d3a8d3e0ab9efc9e68e84115c46811e7a9dc21eca9a87d520
tests/markdown/test_relation_edge_cases.py,test_relation_edge_cases.py,source_code,3952,2025-06-21T00:01:46.203846,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),f8e2016308f84139342cc37ec6e389ffe52d7eb46f998ee327c7f0d5bba3ad9b
tests/markdown/test_task_detection.py,test_task_detection.py,source_code,401,2025-06-21T00:01:46.191444,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),37fe91a689742033a0eb76723557e31c2a85e591133383b3887572cb714db2a5
tests/mcp/conftest.py,conftest.py,source_code,1761,2025-07-08T20:39:18.351554,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),350b2ca42c52ef76e002deddb65fc0eccb9f8989bd26078ea1c100e3b93bae3f
tests/mcp/test_prompts.py,test_prompts.py,source_code,5989,2025-07-01T08:33:22.093691,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),e3b2ee5b4bc120b5043225ebdbf73eadd71be9c3d9d8e1fdd66a24a8ff11a780
tests/mcp/test_resource_project_info.py,test_resource_project_info.py,source_code,4885,2025-07-01T08:33:22.094085,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),6a043736789b239c8d4ae00d1a5332a0fc3bed4038fe3388970ba97dc7431a93
tests/mcp/test_resources.py,test_resources.py,source_code,549,2025-07-01T08:33:22.094481,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),77a6396b9b8a9d3b3d202e438243ca6178d87fd7680c9fc063ee013f2042aa4c
tests/mcp/test_tool_build_context.py,test_tool_build_context.py,source_code,3714,2025-07-01T08:33:22.094661,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),5f0a39fdb652605335ed54aa91fe994262441bb7ba46f381c46ebca882fc23f2
tests/mcp/test_tool_canvas.py,test_tool_canvas.py,source_code,7997,2025-07-01T08:33:22.094982,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),c517bb1f9edc77b76fbeb483dfcb2dd57de9f0b80a10738708bc90cbe388dc9e
tests/mcp/test_tool_delete_note.py,test_tool_delete_note.py,source_code,4208,2025-07-01T08:33:22.095578,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),4cbf3ba4313438651b0608d5b25faf1400f7bd6a78af630351aecd4457800895
tests/mcp/test_tool_edit_note.py,test_tool_edit_note.py,source_code,13503,2025-07-03T18:43:10.084890,Drew Cain,2,Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),dd441969aa54b3739f2d9202d3feaf46aee807ec51e01a13ef41eda52743944f
tests/mcp/test_tool_list_directory.py,test_tool_list_directory.py,source_code,7730,2025-07-01T08:33:22.096737,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),cfbb5c06bcfe74ea60f31051eb9b03ac80b86232b053c188bf3b172132f7c0f5
tests/mcp/test_tool_move_note.py,test_tool_move_note.py,source_code,25851,2025-07-30T09:39:45.727347,jope-bm,2,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co),c9f95158503fa42e37532aaa35027538fa49cea718d059d570cf547d31f01043
tests/mcp/test_tool_read_content.py,test_tool_read_content.py,source_code,19442,2025-07-30T09:39:45.727526,jope-bm,1,jope-bm (joe@basicmemory.com),1817e32f997b06dbdabf02864d0c70584c1cd80bf1fae8dfbef649de79434bc5
tests/mcp/test_tool_read_note.py,test_tool_read_note.py,source_code,22991,2025-07-30T09:39:45.727746,jope-bm,4,jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),4e13ea9ff977525aa62b6a8c22505ffe989329dc3e7d07b71e044cf43ef3ddfd
tests/mcp/test_tool_recent_activity.py,test_tool_recent_activity.py,source_code,3983,2025-07-01T08:33:22.098561,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),45414152cd45334b84df56f653146b7bd2939907548e7245a37207fdccfc4c87
tests/mcp/test_tool_resource.py,test_tool_resource.py,source_code,6782,2025-07-01T08:33:22.098948,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),3916ae1c4d458eee53d2c570b9bbb708ecd617de5a145c3a7552389954bc9195
tests/mcp/test_tool_search.py,test_tool_search.py,source_code,10959,2025-07-03T18:43:10.085177,Drew Cain,3,Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),07839cf8e89d74ba05ec1dc55fa842444c1f0cc3d1587823b0ac3458ad89e87c
tests/mcp/test_tool_sync_status.py,test_tool_sync_status.py,source_code,6331,2025-07-01T08:33:22.100021,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),3f3566df5b9f3c66bb9b3bd77d4c05825480d0e85015305cb1b7ab9429c7a7ec
tests/mcp/test_tool_utils.py,test_tool_utils.py,source_code,9228,2025-07-01T08:33:22.100561,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),b50789f7a78b0169f52dc8bd16e262510ed4a0ac130fce698d4be84af1fe2d31
tests/mcp/test_tool_view_note.py,test_tool_view_note.py,source_code,10004,2025-07-01T08:33:22.101164,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),385535ce791c3ab55db827cbcf27ea25ae85faf3a027f52df4eea052cede7f36
tests/mcp/test_tool_write_note.py,test_tool_write_note.py,source_code,35136,2025-07-30T09:39:45.728137,jope-bm,3,jope-bm (joe@basicmemory.com); Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
tests/repository/test_entity_repository.py,test_entity_repository.py,source_code,15498,2025-06-21T00:01:46.176265,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),8f5d1ff6cff7b640993ff73f463b1e7801a4dd69640fc55fa23dc58b865e9943
tests/repository/test_entity_repository_upsert.py,test_entity_repository_upsert.py,source_code,16983,2025-07-03T18:43:10.085482,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com),ee0ecd6f4ff8b31838bd6de6fb1018af437e02813d8315c06f4e20a6b78a0572
tests/repository/test_observation_repository.py,test_observation_repository.py,source_code,11786,2025-06-21T00:01:46.158862,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),3006d392ad94dcc9b3ae031d63a1af6935624534389dd025de70d4c9e112aa0b
tests/repository/test_project_info_repository.py,test_project_info_repository.py,source_code,1186,2025-06-21T00:01:46.161876,bm-claudeai (AI Assistant),1,bm-claudeai (AI Assistant) (claude@basicmachines.co),dbce7ae7350403cbc37343c2e3e68e4d2255e247006aaa6c495a7ce8bfddea7d
tests/repository/test_project_repository.py,test_project_repository.py,source_code,10511,2025-07-30T09:39:45.728536,jope-bm,2,jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),279a3324635b1b515c18003bd1875cf230fef1192cadef9ca426846783717e75
tests/repository/test_relation_repository.py,test_relation_repository.py,source_code,11980,2025-06-21T00:01:46.167567,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co),fcdbdf40f7b56145b98eedde5ead64ef6187e859adde48cc09f5a97754757428
tests/repository/test_repository.py,test_repository.py,source_code,5996,2025-06-21T00:01:46.152691,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),3a6d4ca0b7a438a30ae504f5eac22874d4e1a937a4246e90efad46c90f0e1c95
tests/repository/test_search_repository.py,test_search_repository.py,source_code,25471,2025-07-03T18:43:10.086041,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),805b5cc55a53ae9d6e86c0de735c7f38ac9fd92c23194b0150fa3e7f5507a146
tests/schemas/test_memory_url.py,test_memory_url.py,source_code,2000,2025-06-21T00:01:46.346515,Paul Hernandez,1,Paul Hernandez (paulmh@gmail.com),946fa6c48534f41c8b9cc27bbad164d3e3673a79afa84fbb439fae4cd26c504b
tests/schemas/test_memory_url_validation.py,test_memory_url_validation.py,source_code,9338,2025-07-01T08:33:22.103247,Paul Hernandez,1,Paul Hernandez (paul@basicmachines.co),7054c3a3cdb8e05d2ffd254ed298908ff3d48a0a3927d960975eae1ab9beeb38
tests/schemas/test_schemas.py,test_schemas.py,source_code,16880,2025-07-03T18:43:10.086337,Drew Cain,3,Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),2c746a00c340f063c160ace67569955a300a294040949695c79fd4ae124fb611
tests/schemas/test_search.py,test_search.py,source_code,3264,2025-06-21T00:01:46.343685,github-actions[bot] (AI Assistant),2,github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com),7437775d1b4a61a438cbabe3dbb1becd2adc4dbcc0c24595830c0843fa7a3166
tests/services/test_context_service.py,test_context_service.py,source_code,8833,2025-06-21T00:01:46.446200,bm-claudeai (AI Assistant),2,bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com),ab49d21a05d5364e7e8a2f00b07aabe1109b7417698e0acd92792e2cb5f22732
tests/services/test_directory_service.py,test_directory_service.py,source_code,6854,2025-06-21T00:01:46.457258,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),a0c765fb6bd1a75b6ba4592b1df344c48273b6ab03ba6bd15d2b5e840eb2d521
tests/services/test_entity_service.py,test_entity_service.py,source_code,59278,2025-07-03T18:43:10.086674,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),d7d495756faa10b415d705cf26b69d2efbc5f190a834b6767386ff296629ea71
tests/services/test_file_service.py,test_file_service.py,source_code,5243,2025-06-21T00:01:46.460141,Paul Hernandez,1,Paul Hernandez (phernandez@basicmachines.co),83e6e9a7584b7baeccbc84865d8c1d3e2a0223e7d2b9c066d3799c289196ab7d
tests/services/test_initialization.py,test_initialization.py,source_code,6939,2025-07-08T20:39:18.351946,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),ebb8d11ff37dcb36c4844c68f873c8e0e8aa6ab1b82abb22ee96c4233e6ab287
tests/services/test_link_resolver.py,test_link_resolver.py,source_code,13618,2025-07-01T08:33:22.105766,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),c9fc56d0c3572770e95ca6175c213ed8b44c2a6ddddc298b5662cc2d8f41014e
tests/services/test_project_service.py,test_project_service.py,source_code,28152,2025-07-30T09:39:45.728920,jope-bm,3,jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),430868ed796518937a3b0bed4d8c33be53264a6100d61d54b0745bb2b58bf29a
tests/services/test_project_service_operations.py,test_project_service_operations.py,source_code,4679,2025-06-21T00:01:46.442141,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),d4103b6c5fff7e74ba84b86033537b8522aa60fc75aec4974d52e1dfec68b1b4
tests/services/test_search_service.py,test_search_service.py,source_code,25299,2025-06-21T00:01:46.439133,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),b3e58258db2b727ab8a1792e52fcb038d3a0779bcd1b70f2d295a9d06e5e2513
tests/services/test_sync_status_service.py,test_sync_status_service.py,source_code,9926,2025-07-03T18:43:10.087266,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),00245ca356e33fc7afcd2d42cb5ec082038c95383d738067821cf1e8d23c9a4b
tests/sync/test_sync_service.py,test_sync_service.py,source_code,39659,2025-07-01T08:33:22.107088,Paul Hernandez,2,Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co),0609aee9906549f27fc4289b5ac2d46a50e53e12a0615a2701f17fbe9a74e169
tests/sync/test_sync_wikilink_issue.py,test_sync_wikilink_issue.py,source_code,1959,2025-06-21T00:01:46.420370,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),db4b98dd7008a1a26b0fd02b9a2d03bca950b281efc19d98a91b017c500b23a5
tests/sync/test_tmp_files.py,test_tmp_files.py,source_code,5930,2025-06-21T00:01:46.424277,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),cead95a07b29f6c1ab933abe67f9648122f5f33d33753c8e4ab4934af96cbc25
tests/sync/test_watch_service.py,test_watch_service.py,source_code,14076,2025-06-26T09:28:51.860679,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),e324a4127f8bfe59798f3a08125488c701e88239b7482d87a0394b91c55b893e
tests/sync/test_watch_service_edge_cases.py,test_watch_service_edge_cases.py,source_code,2169,2025-06-21T00:01:46.409830,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),b87c5015330e9b551630aab7ef95ac1e7663f0ce46e912d8155b319e139559aa
tests/test_config.py,test_config.py,source_code,3680,2025-07-03T18:43:10.087558,Paul Hernandez,3,Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),6a246431cff739274efda895dfa28f38710289a73e645312f5fa05fce0885ec8
tests/test_db_migration_deduplication.py,test_db_migration_deduplication.py,source_code,6166,2025-07-08T20:39:18.352690,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com),ab768e061531ca2edcd598c101b5d884e549f90c7a4b305a23713a199d263fbb
tests/utils/test_file_utils.py,test_file_utils.py,source_code,6199,2025-06-21T00:01:46.219673,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),4615bb20b92b8a79d9e086301f852b34c3360f17ecc209920d68528268ddeed4
tests/utils/test_parse_tags.py,test_parse_tags.py,source_code,1691,2025-06-21T00:01:46.223400,Paul Hernandez,2,Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com),b83afdf18dac8b3411bd0fab1301683217d3d1abceaf2d22c09e79c709efd8eb
tests/utils/test_permalink_formatting.py,test_permalink_formatting.py,source_code,4193,2025-06-21T00:01:46.225738,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),d998a4ca846c78679728a918b6f71df67c30b6961ede3cffa609f2cf6ad160eb
tests/utils/test_utf8_handling.py,test_utf8_handling.py,source_code,5791,2025-06-21T00:01:46.228483,Paul Hernandez,1,Paul Hernandez (60959+phernandez@users.noreply.github.com),50ec792e572af51cd2d996d4ddacbcfd3bbd79aa928818a90c42117451ea829f
tests/utils/test_validate_project_path.py,test_validate_project_path.py,source_code,15346,2025-07-30T09:39:45.729224,jope-bm,1,jope-bm (joe@basicmemory.com),27a4bf2a9d277e6cd773619e5241b20175d211ec60c284361bec404fd99143f3
uv.lock,uv.lock,other,232710,2025-07-30T09:39:36.619155,Paul Hernandez,3,Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co),caa1e3f6e66464b884a78cab77df2fe303fa3c0dc573be0196474b6d3c150581
1 path name category size_bytes modified_time primary_author contributor_count contributors_list sha256_hash
2 .claude/commands/release/beta.md beta.md documentation 3103 2025-07-01T08:33:22.037438 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 345f06e3f304470a8892e96979355919c8e5d8f4212ebbe543c3272fd15a5cbe
3 .claude/commands/release/changelog.md changelog.md documentation 4648 2025-07-03T18:43:10.075727 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) ea2484352f4f881aa71f546dcf84b0882d60c96673a88406aa2f7e12a282a1f4
4 .claude/commands/release/release-check.md release-check.md documentation 3344 2025-07-01T08:33:22.040060 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 7dd41a40e95c89153936c4ea60a3c989eccd5973fff9f6caef156de82f12bd04
5 .claude/commands/release/release.md release.md documentation 2925 2025-07-01T08:33:22.043674 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 0e4afd788fcf85fbe46094452e9b92a4290c3e23702283e72ee56aa9288476e5
6 .claude/commands/test-live.md test-live.md documentation 18720 2025-07-03T18:43:10.076403 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 21a9edeefb3e5673d833151a2fd0464087c0f0a9d056fdd7fca474c1d177d6fc
7 .dockerignore .dockerignore other 623 2025-07-01T08:33:22.048301 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) b3079bffad89d07186c9166725075524ffc07ceea6dc1d53fdbea6dca9585684
8 .github/ISSUE_TEMPLATE/bug_report.md bug_report.md documentation 967 2025-03-01T14:22:50.968994 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 0d34443ddda84a5e9726c8268083c9646d2cc64ff9c5ebf56c89587cef4c7706
9 .github/ISSUE_TEMPLATE/config.yml config.yml configuration 374 2025-03-01T14:23:26.936836 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) f4e21bb9b7f053509b6c08548a2649a5d62f44f0106810aa8c915d2f29acc623
10 .github/ISSUE_TEMPLATE/documentation.md documentation.md documentation 557 2025-03-01T14:23:40.020117 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 78f80a6b9a2b954695f74a847a068e0b780da6020572401c13c931555a4a9db5
11 .github/ISSUE_TEMPLATE/feature_request.md feature_request.md documentation 786 2025-03-01T14:23:06.344081 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) cee834de442221d53f3d2eeb85b81f3a641b3a7cb587ef8a169452305007439d
12 .github/dependabot.yml dependabot.yml configuration 529 2025-03-15T10:40:16.936258 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 8c6c2ef9a1417da63adb49c70f45524129bd5c46bbd7452b6b97cfe65205f89d
13 .github/workflows/claude.yml claude.yml legal 4628 2025-07-03T18:43:10.076958 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) d026f1756310781b85abc491b454cc31b52dfce4b951cdf8a4a555b6b047604c
14 .github/workflows/dev-release.yml dev-release.yml configuration 1405 2025-06-03T08:31:42.915367 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 58bcfc9e11e210e02dee76aabe7d1378da4edafb7f61857049ee6ccee9d45ffa
15 .github/workflows/docker.yml docker.yml configuration 1621 2025-07-01T08:33:22.049870 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c918a86f2b16ebb45182a73c45272bcd6cea7022cc6e0eaeb2b2a24d8eebfe4d
16 .github/workflows/pr-title.yml pr-title.yml configuration 887 2025-02-17T16:29:35.733646 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) b6df62a20235496feba4d9419c85d6e8446c4d447ad69e7f57b51cc8953f68f4
17 .github/workflows/release.yml release.yml configuration 2479 2025-07-03T18:43:10.077261 Drew Cain 2 Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co) 163307a99e2f3d317af6ae8927e1537654d88bf574a6958ac0bcd2d4de266b2a
18 .github/workflows/test.yml test.yml configuration 1426 2025-07-01T08:33:22.054430 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) ab09084a11be8adfc83fc6285c652fbe44a4584aa7d7d3658fcf0c57112d1640
19 .gitignore .gitignore other 566 2025-07-01T08:33:22.055006 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 358658b0faa8c3145e539421deba4ad0bac4a8ea2e608b2c2f867a822c67b36f
20 .python-version .python-version other 5 2024-12-02T16:41:37.012830 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 7b55f8e67b5623c4bef3fa691288da9437d79d3aba156de48d481db32ac7d16d
21 CHANGELOG.md CHANGELOG.md documentation 53647 2025-07-06T10:34:45.550847 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); semantic-release (semantic-release) 0b165eb18975ff253cde08f4687b7808ac6ce63b6d8c10912c495a76c9058d1a
22 CITATION.cff CITATION.cff other 302 2025-02-17T16:29:35.735146 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) bc4878cf6f80bb64655f3f93a5821ffe0924ad8311c784b5f81a36093fcffdda
23 CLA.md CLA.md legal 1342 2025-03-01T14:12:38.435132 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) c238c7ff4f2b8451c6bba1bbacd10b04fb7d41d9a9cd76a62644d8193791d169
24 CLAUDE.md CLAUDE.md legal 12310 2025-07-03T18:43:10.078147 Drew Cain 4 Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); Ikko Eltociear Ashimine (eltociear@gmail.com) 751f10c1a9a9c0ba2054d9c9cd36cf0989d6a347491fd505bb6504ee74b12900
25 CODE_OF_CONDUCT.md CODE_OF_CONDUCT.md documentation 509 2025-02-17T16:29:35.735238 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) b276b464e85eb1e5d9f09d90bba9573508a7bf81d2eeb38d0f25bd9f2fa66198
26 CONTRIBUTING.md CONTRIBUTING.md documentation 7329 2025-07-30T09:39:36.607158 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co) ca822fe39cc529bfc3822d317ab8a20fd141ec3ecb8c20de9e2eca3edce1c282
27 Dockerfile Dockerfile other 856 2025-07-01T08:33:22.056908 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) c45501905423990e31ffdb8ae4e976eca6423c6784905c1036bcc7795b70feef
28 LICENSE LICENSE legal 34523 2024-12-02T16:41:20.739392 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef
29 README.md README.md documentation 16261 2025-07-03T18:43:10.078281 Drew Cain 6 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Jason Noble (github+jasonn@jasonnoble.org); Marc Baiza (43151891+mbaiza27@users.noreply.github.com); Matias Forbord (codeluggage@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 2c4b82b30f49ab465617034fda999157450ed168f8e097fe8e9ca7ac65a197a7
30 SECURITY.md SECURITY.md documentation 303 2025-03-12T16:04:01.989927 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 96932268b8fe737577941b49e9dff5ff8ad59c11cc3f9a016989589ef5c98c1f
31 docker-compose.yml docker-compose.yml configuration 2534 2025-07-01T08:33:22.059281 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 4a9fdfd2f0d42b148fe021eee77fd5e772c7ba9c2fe5d0cc310c8c7a64257417
32 docs/AI Assistant Guide.md AI Assistant Guide.md documentation 16423 2025-07-01T08:33:22.060491 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c717869a3d356852077026591364ecb0589a6d53f9764af027c57d142ea7ff55
33 docs/Docker.md Docker.md documentation 9088 2025-07-01T08:33:22.060976 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) e13d3f50148353046b52d9d493986880f76f2914ef4efe1a759b2cc9d10913a4
34 justfile justfile other 5496 2025-07-30T09:39:36.608008 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com) a8df6c73ee8cf3eaa48e4cba193dffbc2821b843a8cc0c0cd58b8517f2252907
35 llms-install.md llms-install.md documentation 2307 2025-06-29T14:19:28.826375 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 674105eb85c8f1d59fc2ddc6840f2a2a3e293358687a305c37dffc8144304f97
36 memory.json memory.json configuration 90075 2025-06-29T14:19:28.827172 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 22d5eee995ca0548b7d9dd152f9a3690c42a514cbf38fea750b3ed2a17dd99c5
37 pyproject.toml pyproject.toml configuration 3279 2025-07-30T09:39:36.608791 Paul Hernandez 4 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); semantic-release (semantic-release) 428a5d64d4555dc7cd2a81b89a3444bdb1d2f3017257fbc9160bd0ae982718e1
38 smithery.yaml smithery.yaml configuration 433 2025-03-11T16:36:03.389716 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 63f13499cf4fee0751b5005f5bd85597dc1c7f9a881f4ec8147cc23e95e75c2d
39 src/basic_memory/__init__.py __init__.py source_code 256 2025-07-06T10:34:45.551291 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@gmail.com); semantic-release (semantic-release) c946490ab9d64957d25df37f721392024916f8a81ed838f70a8e938b1c2637db
40 src/basic_memory/alembic/alembic.ini alembic.ini configuration 3733 2025-02-23T17:31:23.198433 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 20466c9c5f026db66793006bebb2f328a3686c7bb34da40d52f33c3eca29e717
41 src/basic_memory/alembic/env.py env.py source_code 2838 2025-07-08T20:39:18.336650 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) e241d987eadfcd5011cbd9ddbecb833d306de87937759d81c08d37d04a6906b0
42 src/basic_memory/alembic/migrations.py migrations.py source_code 718 2025-06-21T00:01:59.613806 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 96b8873d70dd04b48d5c45b7413a54d1224ab9577557ef0dad5929377a9fb144
43 src/basic_memory/alembic/script.py.mako script.py.mako other 635 2025-02-17T16:29:35.739659 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 304a8bfb6a804e5493f5300e79882c70c9f5bb2e87512f4d16f0e097dddd323f
44 src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py 3dae7c7b1564_initial_schema.py source_code 4363 2025-06-21T00:01:59.595762 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 9536d69409ddd5eb3bc54f7d0e82607da45ed7f2ad7bc4cbf7cae2a9e28657cd
45 src/basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py 502b60eaa905_remove_required_from_entity_permalink.py source_code 1840 2025-06-21T00:01:59.604717 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 93ac584e660f33d468b3eec203b070641298c282bf82655d0beda7f050237681
46 src/basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py 5fe1ab1ccebe_add_projects_table.py source_code 4379 2025-06-21T00:01:59.601299 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) d82098f5aca3cdbada60c7229ea4ac262f527349eed9ef9e864509bacfaccf7e
47 src/basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py 647e7a75e2cd_project_constraint_fix.py source_code 4210 2025-07-01T08:33:22.063463 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 604ac5908a5976272f52297866d13abb14a99390590935d9fd37cf13e32049fa
48 src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py b3c3938bacdb_relation_to_name_unique_index.py source_code 1428 2025-06-21T00:01:59.598551 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 46c1b2990cdf4575752d234a8b28b494c8a54f15b5360c12f6347aef27b66a92
49 src/basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py cc7172b46608_update_search_index_schema.py source_code 3679 2025-06-21T00:01:59.610909 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 9036af47d42ac7dc12bb93f7a9de1266aff0688b031b550c4e0d92983a24b4c5
50 src/basic_memory/api/__init__.py __init__.py source_code 72 2025-06-21T00:01:59.579981 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) c02a63fb6d63d43d0acca97d425eae9cb055158d0ad6e1a9fc57926512adaa99
51 src/basic_memory/api/app.py app.py source_code 2897 2025-07-08T20:39:18.337081 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ad673ac2c09410af5830f6afdffaa28b0040480dacd52ff01750b8b2c066e8cf
52 src/basic_memory/api/routers/__init__.py __init__.py source_code 398 2025-06-21T00:01:59.549416 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 4443b908a43dea8e6fb4658009cb08c48a56c9b21449e29773cdd159b59cf014
53 src/basic_memory/api/routers/directory_router.py directory_router.py source_code 2054 2025-06-21T00:01:59.567371 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ac1407beec1f7d43a4d222af704b3642559c960bebf2eaf6e1ffe91f7fac5514
54 src/basic_memory/api/routers/importer_router.py importer_router.py source_code 4513 2025-06-21T00:01:59.561600 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) c45502a2b90f5a8f00174c9ad14adc2e65cd7fc0a33d9740a9d750207f0f3bea
55 src/basic_memory/api/routers/knowledge_router.py knowledge_router.py source_code 9738 2025-07-01T08:33:22.064119 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) e1d0ffb4fa5c246582ba844a11b417092de1a173567be55b7060bbc55fbc5681
56 src/basic_memory/api/routers/management_router.py management_router.py source_code 2730 2025-07-08T20:39:18.337438 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) cdbce294dcec6146c56c4dae15744cdf77039fd21b23eef7cbc0b5f9bfb5f4ee
57 src/basic_memory/api/routers/memory_router.py memory_router.py source_code 3003 2025-07-01T08:33:22.064510 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6bd0a7c775e0c1290efb6001173237c0cdcfa0c1b1bb27c9145a7b35f1596a97
58 src/basic_memory/api/routers/project_router.py project_router.py source_code 8444 2025-07-30T09:39:45.721369 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6539c69f1d506c65d14cc2c151876b19057f37d84d51a9e6f464ed47a4692648
59 src/basic_memory/api/routers/prompt_router.py prompt_router.py source_code 9948 2025-07-01T08:33:22.065221 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) e30c6aebe35112054933c37d0b462c3750005030fc9e44c23b35b2cd2a93485c
60 src/basic_memory/api/routers/resource_router.py resource_router.py source_code 8058 2025-06-21T00:01:59.570399 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 584244a84698ff24ca8f9f94fab5b891740a51c25f972920c08ebf83f478d5c9
61 src/basic_memory/api/routers/search_router.py search_router.py source_code 1226 2025-06-21T00:01:59.543020 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 183eb68e50904e22ff54db1d89b8be6f57fa1f8d0a096a3d497d8297b607e105
62 src/basic_memory/api/routers/utils.py utils.py source_code 5159 2025-07-03T18:43:10.079143 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 9e60f57da24e1dc9d64236c26a7a2353003dc618a77fbeb853c494aa3357a57c
63 src/basic_memory/api/template_loader.py template_loader.py source_code 8607 2025-06-21T00:01:59.576132 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 7b16d3797c89460c8b1664a335eae8c634fb5f60d6166d97e6a5156b776b6d83
64 src/basic_memory/cli/__init__.py __init__.py source_code 33 2025-06-21T00:01:59.456735 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 6ab70a2c05910e13c3ef1e6df34325be265e633c076741998a0cb2dcd295a069
65 src/basic_memory/cli/app.py app.py source_code 2268 2025-07-08T20:39:18.337708 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) f710670380d707c4458ad881f7a25bb3c5cdb2bcff59f139f37421264fe8f2f8
66 src/basic_memory/cli/commands/__init__.py __init__.py source_code 393 2025-07-08T20:39:18.338070 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) de8a23702f98fb844fa9f7fdbedc16ce24ff4f8baf05589c2f4a521cd8a55645
67 src/basic_memory/cli/commands/db.py db.py source_code 1480 2025-07-08T20:39:18.338326 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 704a1096d80ab9d12e247d029fe6223e935a0d0ceee7e615c020f46a720828e0
68 src/basic_memory/cli/commands/import_chatgpt.py import_chatgpt.py source_code 2856 2025-07-08T20:39:18.338787 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 8957cca3acab6351335624a518bdc19d956487e93d86dd1b6c2309e9d5850ae5
69 src/basic_memory/cli/commands/import_claude_conversations.py import_claude_conversations.py legal 2946 2025-07-08T20:39:18.339053 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7bc97838732bf00f4fb4a80e3ba4fdfbce8971ae85cc2ac902c3b3a3e110ae57
70 src/basic_memory/cli/commands/import_claude_projects.py import_claude_projects.py legal 2891 2025-07-08T20:39:18.339310 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6321577075801c99ad47a0cdc13b5aa3c9ca102a05ca8f06ae2a51125a8c43bc
71 src/basic_memory/cli/commands/import_memory_json.py import_memory_json.py source_code 3013 2025-07-30T09:39:45.721637 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7ea193f6720b2672290ad16c79a143a31d88a1d567838f12b9ebf9110a7c0338
72 src/basic_memory/cli/commands/mcp.py mcp.py source_code 2593 2025-07-08T20:39:18.340025 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 3e1be3d27b18ef4743204b7cf8a37fbc2a1a91fc1636c468e08cefcd8597bac6
73 src/basic_memory/cli/commands/project.py project.py source_code 13836 2025-07-30T09:39:45.721838 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) af3e6f30410ab48355f4b1a46aaa3aa5423cb0a0490f51447db1d25e09f39916
74 src/basic_memory/cli/commands/status.py status.py source_code 5809 2025-07-08T20:39:18.340324 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) b6d0ea237d714d054f69b24a2058beba74e224a2fa4a80a525d62a2aa2ea2d70
75 src/basic_memory/cli/commands/sync.py sync.py source_code 8213 2025-07-08T20:39:18.340627 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 703b3e73c4c33850c27f6f4c07abbf5d4ce5dc43f706bf0c43264bbb45f8b213
76 src/basic_memory/cli/commands/tool.py tool.py source_code 9540 2025-07-01T08:33:22.070903 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 9b2fa400b9f7921bf55b602f8bbdfa36b1ec7e4a5bc8fe7b9838b92e31f0a9ed
77 src/basic_memory/cli/main.py main.py source_code 465 2025-07-08T20:39:18.340868 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7a405b12bff98e319968526238ae15859df0362bba5a3857562fecca47b5c70d
78 src/basic_memory/config.py config.py source_code 12487 2025-07-30T09:39:45.722031 jope-bm 5 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 86341330a041b94d38f5e4f1b94f3509d74351c67cc826af1a4ff57313e5f726
79 src/basic_memory/db.py db.py source_code 7428 2025-07-08T20:39:18.341550 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 25e053b0b3f9e8fa823b05fc099001efe8a3a4f6e0f80728e5079ba45aa406d8
80 src/basic_memory/deps.py deps.py source_code 12144 2025-07-08T20:39:18.341870 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) bce28cd9ca549b8afce0bc689cc1fb2dc80cd5b929f85114aa682bb8ae82411d
81 src/basic_memory/file_utils.py file_utils.py source_code 6700 2025-06-21T00:01:59.587200 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 79ac5328b2c46d3232fcc6ff22ffc39ade085c0252ad91958be2a7ae9c9c8b71
82 src/basic_memory/importers/__init__.py __init__.py source_code 795 2025-06-21T00:01:59.295471 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 0537015bdecfded85cb166b9c3db50b2f3aef329c70e0c36fbcb4f824099a21f
83 src/basic_memory/importers/base.py base.py source_code 2531 2025-06-21T00:01:59.305335 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 6b0c1efd4f827f348834aa0cea2ae8eebbb842a2e5b1f5f3747ced0ef79b9f13
84 src/basic_memory/importers/chatgpt_importer.py chatgpt_importer.py source_code 7372 2025-06-21T00:01:59.308956 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) dfa540b196ab23b5c4e6bfcac4da567873351cf7c78fa35f4c3b87e84c58e218
85 src/basic_memory/importers/claude_conversations_importer.py claude_conversations_importer.py legal 5725 2025-06-21T00:01:59.288540 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) a7bc9e872f4f81ce5f79fe9dd1a6fd0f00a6f020a2cb2664184a97f14eeb1dbc
86 src/basic_memory/importers/claude_projects_importer.py claude_projects_importer.py legal 5389 2025-06-21T00:01:59.291737 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) a452675fd9bb18ebf64eb4bd7f69ccd7e993b617931015a3c4ab700d67493863
87 src/basic_memory/importers/memory_json_importer.py memory_json_importer.py source_code 4631 2025-07-30T09:39:45.722316 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) bc7d045299f17ed9ad5cebff7b1423250ec28a11130e47a0ac48d3ab82bdeafc
88 src/basic_memory/importers/utils.py utils.py source_code 1792 2025-06-21T00:01:59.298428 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) ee7e22fd4812362394f62ed8944abe80738c1e3ab88131faf639f4a8ccfd6c89
89 src/basic_memory/markdown/__init__.py __init__.py source_code 501 2025-06-21T00:01:59.316337 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 0ddce2a025ad0e729aab4e411d82e02ffefc15ac041cba4b5e7a7e90f4957c87
90 src/basic_memory/markdown/entity_parser.py entity_parser.py source_code 4516 2025-06-21T00:01:59.321461 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 1b0d1177358645ccbadd170b3f62760dc9bd838d38c741978ab0da5836234d68
91 src/basic_memory/markdown/markdown_processor.py markdown_processor.py source_code 4968 2025-06-21T00:01:59.325241 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 4b99f2ebdceed9d96a3bbb562682eba4b4b383c7a64080eaec3bbba25a4952c9
92 src/basic_memory/markdown/plugins.py plugins.py source_code 6639 2025-06-21T00:01:59.312733 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 82d2332918e866ccaf06a2e954d9eb9b397f71b4d9e591a7f2472e5f1423464a
93 src/basic_memory/markdown/schemas.py schemas.py source_code 1896 2025-06-21T00:01:59.329982 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 7b2c580abd615725a62267257b46ac13922dfc30fa01192a681658bb528ae67e
94 src/basic_memory/markdown/utils.py utils.py source_code 3410 2025-07-03T18:43:10.079889 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 1b757f0d06e60e3ea276c0b2ea44fe1a156a895e093c1e607d6286e702bf4ae4
95 src/basic_memory/mcp/__init__.py __init__.py source_code 35 2025-06-21T00:01:59.404757 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 76c0ce84aaa361e21b0942db1c8c5f708b536eab9d12e120d4da7ce9eab41844
96 src/basic_memory/mcp/async_client.py async_client.py source_code 925 2025-07-30T09:39:45.722547 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 6531f4387f30a30a322197ff51d6eacae4b25a58c648095931b3d1145d1da303
97 src/basic_memory/mcp/project_session.py project_session.py source_code 4203 2025-07-08T20:39:18.342906 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 4da403ec93dec9063eeb8b05246e3501740d162cc77a067d3ce54ba43d690819
98 src/basic_memory/mcp/prompts/__init__.py __init__.py source_code 615 2025-07-08T20:39:18.343263 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) f8197d0e08f64c3f4f50b8f38203ea5eebcf123582472ed2f58834de1dbe53b0
99 src/basic_memory/mcp/prompts/ai_assistant_guide.py ai_assistant_guide.py source_code 821 2025-06-21T00:13:53.262832 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) f13239c4e6e2455730bfac3d6f2d71407957d3086fc84efe2c6b22a833114c58
100 src/basic_memory/mcp/prompts/continue_conversation.py continue_conversation.py source_code 1998 2025-06-21T00:13:53.263309 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) aec9a50b657b7bb1ba0c02b42bcdb9bc5b0f2a0b1143bd361c5ce7ea59076833
101 src/basic_memory/mcp/prompts/recent_activity.py recent_activity.py source_code 3311 2025-07-01T08:33:22.073986 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) d2fd5cddbd927430f15d5b85f1e3a3368a18cb4e2e4587a5d29749d2b9e0830e
102 src/basic_memory/mcp/prompts/search.py search.py source_code 1692 2025-06-21T00:13:53.264093 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 9dbf3c319cbdb5d5bf32608b51522d8ae92b2dd6f7c441d62efd0954b525738a
103 src/basic_memory/mcp/prompts/utils.py utils.py source_code 5431 2025-06-21T00:01:59.425042 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 55a72b6eac18b724a9225608aca1e8e6cea3b684ccb1c609aab1511f7ce90ba4
104 src/basic_memory/mcp/resources/ai_assistant_guide.md ai_assistant_guide.md documentation 14777 2025-03-28T19:12:38.653627 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) aa76160e46256fe2662ae3a86799659916acfede1d5835c1fe2f0b136efb2e0b
105 src/basic_memory/mcp/resources/project_info.py project_info.py source_code 1906 2025-06-21T00:13:53.264918 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 2dc5244f1e225c17d4e8ba784d5721efc3aa2e8a5b3b2e258f22a77ebe155eca
106 src/basic_memory/mcp/server.py server.py source_code 1248 2025-07-08T20:39:18.343419 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 0a910cae27d61ea923e90fbe2d4b5c7cacf3b29d3e156f0b7f77872ab973c761
107 src/basic_memory/mcp/tools/__init__.py __init__.py source_code 1619 2025-07-03T18:43:10.080029 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co) 872b771dd52ec3b763645a2baf6e10a70f049ce3106830a6d3f053b3e09a5fe6
108 src/basic_memory/mcp/tools/build_context.py build_context.py source_code 4611 2025-07-03T18:43:10.080426 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 724280b77b8f5f3e61cd3fdeebc3ee66e2bcfedaaea3638e6a2e2e33fcb1978e
109 src/basic_memory/mcp/tools/canvas.py canvas.py source_code 3738 2025-07-01T08:33:22.076204 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) db617d1bd81f3dbfa5f22d41e6b6b825afe1f73618f37acf63d9830390b98246
110 src/basic_memory/mcp/tools/delete_note.py delete_note.py source_code 7346 2025-07-01T08:33:22.076726 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) b52c9173f5600662f255e7a70a5c17d5293ef8b29c19a8403334d7da62b65c8b
111 src/basic_memory/mcp/tools/edit_note.py edit_note.py source_code 13570 2025-07-01T08:33:22.077181 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) ab8c7e7fbfa3fe5fb0ce6d7bf805454f5fd6182a340aae25237b1e6127b6d5a6
112 src/basic_memory/mcp/tools/list_directory.py list_directory.py source_code 5236 2025-06-21T00:13:53.267453 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) f85c43b02aeee580f4d8ce2a9100c0bab1095b245a0bb608e1847ace0d1ab51f
113 src/basic_memory/mcp/tools/move_note.py move_note.py source_code 17541 2025-07-30T09:39:45.722919 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 5ef27d9baaebc2c5656fc49c4798a4e4e40a70b6cd9d1e761dbcae021bad558c
114 src/basic_memory/mcp/tools/project_management.py project_management.py source_code 12944 2025-07-06T10:34:45.552403 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co) cdac736d65129f689a85c6b82f8e043bc84a31657ead7a8331771171eeaa860f
115 src/basic_memory/mcp/tools/read_content.py read_content.py source_code 9125 2025-07-30T09:39:45.723469 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 96a13addac507fd874394dfa2e1e5ed71af5eb89d0872c1d68cacf9cac3f8724
116 src/basic_memory/mcp/tools/read_note.py read_note.py source_code 8132 2025-07-30T09:39:45.723828 jope-bm 5 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) dff53ce02b16d9c5fca315b3f09bb70867a89d614b0ef3f0548dce87978048ee
117 src/basic_memory/mcp/tools/recent_activity.py recent_activity.py source_code 4833 2025-06-21T00:13:53.269120 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 5d58cd2402679b1bf3c7dfcbb3503e40e776c9347ba499524d3b91c528af98de
118 src/basic_memory/mcp/tools/search.py search.py source_code 15883 2025-07-03T18:43:10.081557 Drew Cain 4 Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 8519b0057468c4450eb543a2f561bcd0d7cb96e1ce1b95e948e02b309ba6b7ca
119 src/basic_memory/mcp/tools/sync_status.py sync_status.py source_code 10517 2025-07-08T20:39:18.344231 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 73dd73b186eaec8b82e6b17e39a67c27082d24e0653dc4e4b35712bd83b80a22
120 src/basic_memory/mcp/tools/utils.py utils.py source_code 20892 2025-07-30T09:39:36.616253 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) a95004911e276822eba88a3fef15c5b9ba861b1ca9a2ecfe0c1e3fb2f4ef0116
121 src/basic_memory/mcp/tools/view_note.py view_note.py source_code 2499 2025-07-01T08:33:22.080024 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 75d357c72113b1d0394987e521a4158ff09b77b23b08b56cddab5144331b1a68
122 src/basic_memory/mcp/tools/write_note.py write_note.py source_code 6612 2025-07-30T09:39:45.723985 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 771396fb49d397feb5f7e36321d4d233c22c4d1b53414912c849957c6e96fc83
123 src/basic_memory/models/__init__.py __init__.py source_code 333 2025-06-21T00:01:59.437212 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 8f40b876d162f85384690291f1d416106f9d26d750d793414e2260e276c85cd5
124 src/basic_memory/models/base.py base.py source_code 225 2025-06-21T00:01:59.447249 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) e2101727c084d519e32a16f6de53ddf9033b1bf15721d4e8c0c27d6d18b1b295
125 src/basic_memory/models/knowledge.py knowledge.py source_code 7087 2025-06-21T00:01:59.444376 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 005c5f292f1f45ae372aadc48c9080b9fa6d7b854d0bb7ecf587ec843ac1e28d
126 src/basic_memory/models/project.py project.py source_code 2773 2025-07-01T08:33:22.080907 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) a14ad06943aeeff9ae4a5fa2dfc0e1d07ce6085acc02dc20c402c3513b7d9397
127 src/basic_memory/models/search.py search.py source_code 1300 2025-06-21T00:01:59.440923 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 3e143cc38b5a0294af8e1d43a4f841e1c1fd193b76136a68f83159ce19e86646
128 src/basic_memory/repository/__init__.py __init__.py source_code 327 2025-06-21T00:01:59.256363 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 3162bea3c42292accea5ee52c8f6ca4368a8079056034529cfae6d820f84d035
129 src/basic_memory/repository/entity_repository.py entity_repository.py source_code 10405 2025-07-03T18:43:10.082570 Drew Cain 3 Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) e2a8d1eba6c8d64bc61d7168df0fe8c29a670858bf9daeac464a0f440273fae0
130 src/basic_memory/repository/observation_repository.py observation_repository.py source_code 2943 2025-06-21T00:01:59.269646 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) aa132f1cb4a36a84f715afdc40e2ac4f98d83e3eba1974b2b4404cc0b020ca04
131 src/basic_memory/repository/project_info_repository.py project_info_repository.py source_code 338 2025-06-21T00:01:59.252582 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) f172d50182a405643a19b2a3d62a80f4e2b41463c775394eb3b66d7de51ff6df
132 src/basic_memory/repository/project_repository.py project_repository.py source_code 3803 2025-07-30T09:39:45.724344 jope-bm 2 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 9083ba214b2bcaaf98164d9c33cda7add52bc77f7269ecb6e43cdd5088728b7c
133 src/basic_memory/repository/relation_repository.py relation_repository.py source_code 3179 2025-06-21T00:01:59.246745 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) cfb3a8e59cff27e063e91bd00e949647bdd92e4d9fc46edeee3acc6de15e26f4
134 src/basic_memory/repository/repository.py repository.py source_code 15224 2025-06-21T00:01:59.273355 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 3096fe71bf106506cbf86aeafe2aafe0aabbe5a5f6c90a212c8aa1e53e1f171c
135 src/basic_memory/repository/search_repository.py search_repository.py source_code 21936 2025-07-03T18:43:10.082844 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) a972f73d1b71dac5770e8eb3793c6c9ac44e4e792f9dab528f8c4e6c6a80bcaa
136 src/basic_memory/schemas/__init__.py __init__.py source_code 1674 2025-06-21T00:01:59.514555 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 98480815c75379bfafe32d2090e87fa40e73caa19b664fbd5db5ea9528ba58cb
137 src/basic_memory/schemas/base.py base.py source_code 6738 2025-07-01T08:33:22.082193 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 171f7b0c4ab33abef2f7379eb1e3bda950586ce7edff83907fd12255f84e267e
138 src/basic_memory/schemas/delete.py delete.py source_code 1180 2025-06-21T00:01:59.502563 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 50047624af7d58c8f780ffb2a065a51c3dde64492f9534917fc42860813e59fc
139 src/basic_memory/schemas/directory.py directory.py source_code 881 2025-06-21T00:01:59.529023 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 17dfcbac9a91a9bfe43b4f060ca2735cb6f69e16d81b63dd554a397830ff29fe
140 src/basic_memory/schemas/importer.py importer.py source_code 693 2025-07-30T09:39:45.724633 jope-bm 2 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ac33df423ca32b28ce7b6ea9c29d541f8783a86c0c29f78db35163bf93f139cd
141 src/basic_memory/schemas/memory.py memory.py source_code 5833 2025-07-03T18:43:10.083167 Drew Cain 3 Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) acb4a953a553feca672c4895798a7d948ec51f922f75b66dd7d55716e3bebed3
142 src/basic_memory/schemas/project_info.py project_info.py source_code 7047 2025-07-01T08:33:22.083248 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7dc3635297b6e7fe6e3262b2d78da26f7a79a846a4cecd5624f2e4828979cf2c
143 src/basic_memory/schemas/prompt.py prompt.py source_code 3648 2025-06-21T00:01:59.525753 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 4a92157d9a6b413f04e6e3f8d23dc2a417369c729f9703a8de2643ec114f2071
144 src/basic_memory/schemas/request.py request.py source_code 3760 2025-06-21T00:01:59.511789 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 32fe44beb2d99452223ebf1d3a3a3fe105ef92c79885023b71dfd7db30ecc503
145 src/basic_memory/schemas/response.py response.py source_code 6450 2025-07-03T18:43:10.083478 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 5eea4660a2abe48d83ed083d1c2483fdcfb403e0b504f03c14d22f1cae866abb
146 src/basic_memory/schemas/search.py search.py source_code 3657 2025-06-21T00:01:59.522804 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) cb032c0c60102b6b0ed934f995cf9d6bf93aece296d71d537a75e8ae61c75afe
147 src/basic_memory/services/__init__.py __init__.py source_code 259 2025-06-21T00:01:59.655059 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 5c6b7c597ddf5ffd0af4bdfb32ccbc1c5f2794c658206dee43a9945fafe226d8
148 src/basic_memory/services/context_service.py context_service.py source_code 14462 2025-06-21T00:01:59.672086 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (paulmh@gmail.com) e1178b005e6a89f03d6b239e3c6b152aeb30d535a3f280734440a3aec16228f2
149 src/basic_memory/services/directory_service.py directory_service.py source_code 6017 2025-06-21T00:01:59.648501 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) fd838f5ec79033892777b3c8140868f4b57f13e1658d513959524a434b9f959b
150 src/basic_memory/services/entity_service.py entity_service.py source_code 30440 2025-07-03T18:43:10.083956 Paul Hernandez 4 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 7cd5163eca6b8a0772e838c8c869e478166763cd6a2d745b0b9aa5c25a65baee
151 src/basic_memory/services/exceptions.py exceptions.py source_code 390 2025-06-21T00:01:59.668908 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) a158d0af9d1742a9c5ab5f8c34a062948d9286d1c3c72a5abf20e4d547b21e1c
152 src/basic_memory/services/file_service.py file_service.py source_code 10059 2025-06-21T00:01:59.641785 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 8c2ae69c4913438b7d1c5ecbfcce812fbb5d0ea8e3cedcbd8694e8f40cf086f3
153 src/basic_memory/services/initialization.py initialization.py source_code 6715 2025-07-08T20:39:18.344482 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 4dde58b799cf4ac90631e59949b55b3355a93bdf99df0d9c2408c0cea67e10c4
154 src/basic_memory/services/link_resolver.py link_resolver.py source_code 4594 2025-07-01T08:33:22.084908 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) d7efd516cbea753e6b5411def09aeaeb7539f5743487119ecf163c736e138a8c
155 src/basic_memory/services/project_service.py project_service.py source_code 30125 2025-07-30T09:39:45.725079 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) bce95c00aa9a1da0a8c8090ee34b5b1494207f8c99e6c25de4ec43fddc38f6be
156 src/basic_memory/services/search_service.py search_service.py source_code 12782 2025-06-21T00:01:59.665439 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7392b2d2e7f3ed83c5807855ccd450e0e79c17f254aedee700bce9323a1b5b83
157 src/basic_memory/services/service.py service.py source_code 336 2025-06-21T00:01:59.636167 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 57e77ff20395d3bcc62100e92fe2acaacdd937d977a9fdc764e2b57ff60d4da8
158 src/basic_memory/services/sync_status_service.py sync_status_service.py source_code 7504 2025-07-03T18:43:10.084229 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 0a025d689e8e16f1632872104952105fc904537f3d32bcff592eb1f1dc76fbb7
159 src/basic_memory/sync/__init__.py __init__.py source_code 156 2025-06-21T00:01:59.621137 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 0951e0b981f8e7b876bb6c6833c2af3a29490bbd5726567ea9487c947723623e
160 src/basic_memory/sync/background_sync.py background_sync.py source_code 726 2025-07-08T20:39:18.345062 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 549af64ae91128b76c6df07ef517b82de85ca4ad796be44b5c0141fac01d4513
161 src/basic_memory/sync/sync_service.py sync_service.py source_code 23387 2025-07-01T08:33:22.086507 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 0310b927561370f593980d07773bce641b618b8fbf2d9e3890d174290a0344fc
162 src/basic_memory/sync/watch_service.py watch_service.py source_code 15316 2025-06-26T09:28:51.859209 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) 240ba6ac7523575945f4db442b7da3820d26c9605f2d7a2d357c4e35e215f523
163 src/basic_memory/templates/prompts/continue_conversation.hbs continue_conversation.hbs templates 3076 2025-07-01T08:33:22.086792 Drew Cain 2 Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) b6bac31d25c0e52d0909b2273285092f4e31bc219107f92ed511cd407b65e992
164 src/basic_memory/templates/prompts/search.hbs search.hbs templates 3098 2025-05-25T10:07:54.520119 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 1f570222c1caa78542d46ac7d8a79407ca467b9bd714fa9bd953e8b72a667820
165 src/basic_memory/utils.py utils.py source_code 8654 2025-07-30T09:39:45.725428 jope-bm 5 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); andyxinweiminicloud (andyxinweimin263@icloud.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 111f343e3367339730e081c08acf4613667be574f946126513b5d82da3086bed
166 test-int/conftest.py conftest.py source_code 8203 2025-07-08T20:39:18.345735 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 7231e1bc76cddc7c94433e853825dc0165b103bedf996c2c24eda6ae32fff5ad
167 test-int/mcp/test_build_context_validation.py test_build_context_validation.py source_code 6469 2025-07-08T20:39:18.346021 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) ee61d6b15cee0194fd74c1bcf9361d5a839f6081c7d571ef081da8fcefe1bbf2
168 test-int/mcp/test_delete_note_integration.py test_delete_note_integration.py source_code 13175 2025-07-08T20:39:18.346366 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 497495249b8bcf0af177d8705c6899875460707697980434e560cfe56f86feb3
169 test-int/mcp/test_edit_note_integration.py test_edit_note_integration.py source_code 19822 2025-07-08T20:39:18.346730 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 9ba704badd05808aee0b5a686b7e250e1c865229ec92dab561cba5287b6f6fc6
170 test-int/mcp/test_list_directory_integration.py test_list_directory_integration.py source_code 14744 2025-07-08T20:39:18.347106 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 2c4336a59de63f17ed252bb52d1330ebca901ad83bfebc9e5aa67f132a3c77c6
171 test-int/mcp/test_move_note_integration.py test_move_note_integration.py source_code 19825 2025-07-08T20:39:18.347344 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) cbb17688d0f9551306a7520b059ace7f7dfba70fc20bf0ec98b1148000d7ad7d
172 test-int/mcp/test_project_management_integration.py test_project_management_integration.py source_code 33175 2025-07-08T20:39:18.347658 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 93a43cd699453a7602a5def839740ba5027ab914eba3e7b66a026860ed6bd567
173 test-int/mcp/test_project_state_sync_integration.py test_project_state_sync_integration.py source_code 7728 2025-07-08T20:39:18.347907 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 80b0f5b5cd359498dcbd3af7dcb6194afa4582d8efb3faa38d28675ee45e0748
174 test-int/mcp/test_read_content_integration.py test_read_content_integration.py source_code 11187 2025-07-08T20:39:18.348233 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 6955dd343b40df088ac79175e52723501d83796335a16e43b7f58e16e757e1b8
175 test-int/mcp/test_read_note_integration.py test_read_note_integration.py source_code 1381 2025-07-08T20:39:18.348463 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) b93050da68d0e9d47d46879a386567f688399e28390f4eb31ee37353dc27514b
176 test-int/mcp/test_search_integration.py test_search_integration.py source_code 14690 2025-07-08T20:39:18.348959 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) e0957ada9dd9b12eae5843a37d241ec9293ca929f18281e31197dae36bff589d
177 test-int/mcp/test_write_note_integration.py test_write_note_integration.py source_code 9472 2025-07-08T20:39:18.349398 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) b2b56fee8351168d0c65640c295d969dc4575d9bea87927b2dc07e591cabdff3
178 tests/Non-MarkdownFileSupport.pdf Non-MarkdownFileSupport.pdf other 106751 2025-02-23T17:31:23.206428 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 4aa96f9d0ee830ef6a1ec92243f302bbe2fa8971b84945f4eb5e276bf1ffde96
179 tests/Screenshot.png Screenshot.png other 190076 2025-02-23T17:31:23.207771 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) dd82612fe013c39810d28ae0f2bbcca6ab2c9925b035d7720bb02493ddf5d0a9
180 tests/__init__.py __init__.py source_code 141 2025-06-21T00:01:46.216456 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 2ac5605e69ad8829e96b3689632536539f1cdd3149a29c1903f25af0793a5d65
181 tests/api/conftest.py conftest.py source_code 1404 2025-06-21T00:01:46.359728 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) ea46cde47a37c1f43fe079e48f5d0de8007155a0187f4445abc4f2ffde9c1791
182 tests/api/test_async_client.py test_async_client.py source_code 1382 2025-07-30T09:39:45.725666 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) a2f96226a75e5cff2326ca48ddccea4915964d83e529f7b99958bc233ccdc31a
183 tests/api/test_continue_conversation_template.py test_continue_conversation_template.py source_code 5152 2025-06-21T00:01:46.390901 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) cca08b0291db0ad693c60d8dc203660021feab46d9eb530be7f67e0ebb52c1d3
184 tests/api/test_directory_router.py test_directory_router.py source_code 9376 2025-06-21T00:01:46.356659 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 428626589f0010512481ba495368b0bf2b9617b22764e588eaa3562e86da4d37
185 tests/api/test_importer_router.py test_importer_router.py source_code 16578 2025-06-21T00:01:46.368070 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 03edf6e7404c7350afe76e54ea2bf0d66f668bcdb49f59702067a67b3694309d
186 tests/api/test_knowledge_router.py test_knowledge_router.py source_code 45771 2025-07-01T08:33:22.090490 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 43a5f91ae1fb9038e65c09701336fbdc4dbf40c8319c94727e6312fda9d6d56e
187 tests/api/test_management_router.py test_management_router.py source_code 6220 2025-06-21T00:01:46.402764 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) f6a3f41f263f10c08281525571e2e39fc9ae818ebb7e9895e3f3f3115ea05677
188 tests/api/test_memory_router.py test_memory_router.py source_code 5555 2025-06-21T00:01:46.399667 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) 2b8782015c893081fc67b7f13a7255d4e3dc45b27705a524f94918a339e2d486
189 tests/api/test_project_router.py test_project_router.py source_code 13288 2025-07-30T09:39:45.725847 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) af85d0870069d3831a9214372853994e5d12dc910e37298eb9958f72f6a8bb31
190 tests/api/test_project_router_operations.py test_project_router_operations.py source_code 1812 2025-06-21T00:01:46.381705 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) afd40879de3d7c508cca0feeae3cb7a8f3afcfbb06f59a6104189b1e3291376a
191 tests/api/test_prompt_router.py test_prompt_router.py source_code 5046 2025-06-21T00:01:46.396588 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 9115995b7dd8b44d8a9c06c5e1bbb177618286675a769889e35ac16f26fd9897
192 tests/api/test_resource_router.py test_resource_router.py source_code 13960 2025-06-21T00:01:46.384919 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ad3cb2cabaeab82a7e64253ceede7c1791e2bd70c92f60bc5f3be939292ca22d
193 tests/api/test_search_router.py test_search_router.py source_code 6409 2025-06-21T00:01:46.375427 bm-claudeai (AI Assistant) 3 bm-claudeai (AI Assistant) (claude@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (phernandez@basicmachines.co) ba3e3809795e72db9dd9c8c729048bdbbbdb897c2a5aa8b175112b5d4b56d152
194 tests/api/test_search_template.py test_search_template.py source_code 5258 2025-06-21T00:01:46.378721 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 8f0d16a1de947d0466d96afed02e695f7648adac70b9ba99753250068207a686
195 tests/api/test_template_loader.py test_template_loader.py source_code 8035 2025-06-21T00:01:46.406694 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 4fd7c6c4862cc0d877d29244ecdc0b52a0693fc4574a9667663bee36bfd36b62
196 tests/api/test_template_loader_helpers.py test_template_loader_helpers.py source_code 7445 2025-06-21T00:01:46.372114 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) 8dd35f11c9b3f2d96ac36263edf4430308728c04c9da543b1cb845b96ab20aa7
197 tests/cli/conftest.py conftest.py source_code 1185 2025-07-01T08:33:22.090848 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c3c73da7ad73224931e1fba4ebd70792221be607bfb1488bb5c9420bba03bdb8
198 tests/cli/test_cli_tools.py test_cli_tools.py source_code 13528 2025-06-21T00:01:46.318964 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) befb8cab50132e26a1b13b82ce8e93a3258282cf157a4230214bfcefeae65102
199 tests/cli/test_import_chatgpt.py test_import_chatgpt.py source_code 6719 2025-07-08T20:39:18.350234 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) df447335545248e2fb2417af33f8cc2f0143bd413a1783e41f89605de305b7c8
200 tests/cli/test_import_claude_conversations.py test_import_claude_conversations.py legal 5028 2025-07-08T20:39:18.350607 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ac4b3b9dfc331fa7fa32a2941c027ff8b6585a94a5ccfaaf0a4affe5cf19c00c
201 tests/cli/test_import_claude_projects.py test_import_claude_projects.py legal 4585 2025-07-08T20:39:18.350885 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 7eaab3d491d50ca59c8ba015ecd5740a5feae94d970ee17559c6df9d8558c2cc
202 tests/cli/test_import_memory_json.py test_import_memory_json.py source_code 4891 2025-07-30T09:39:45.726290 jope-bm 3 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (60959+phernandez@users.noreply.github.com) d6734ce059dacf97bc764d848b2e2a6ba52fa96871bc331ec9d2ddc60b766bb6
203 tests/cli/test_project_commands.py test_project_commands.py source_code 6578 2025-07-30T09:39:45.726703 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 51e2ac7ff44b0d1a1db6da406c5c2e6aebceb1b63a1e8d5ab51bbaa834507157
204 tests/cli/test_project_info.py test_project_info.py source_code 3874 2025-07-01T08:33:22.091600 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 0b08950e95a5f276f14c56f6fe99af4d41b8f04548e9b7746f807c5ac4155578
205 tests/cli/test_status.py test_status.py source_code 4160 2025-07-01T08:33:22.091978 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) a1ff89fdac4bf57ba03be92ac79b8ca3210af0b1132d5a2bd0fc0c253fcbf988
206 tests/cli/test_sync.py test_sync.py source_code 3864 2025-07-08T20:43:47.816788 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 77e0f6b42b584a5acfda3c2ff194ff5e71ca57a40cb4484d563f5482bbbb4c0f
207 tests/cli/test_version.py test_version.py source_code 289 2025-06-21T00:01:46.305368 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) db95665178a32d391bd83011d073db269cefba9da8a4dc4d3e392d47bc0e0a8f
208 tests/conftest.py conftest.py source_code 15286 2025-07-08T20:39:18.351368 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) c6f38f103ce58144ffe4562f0c47dad849974e96327f02805e854447e19bc411
209 tests/importers/test_importer_base.py test_importer_base.py source_code 4329 2025-07-08T20:39:00.549594 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) da872a073f2b90fda3c98c9f44cf45884f4f682d91ec1b1f931d42c34eee1939
210 tests/importers/test_importer_utils.py test_importer_utils.py source_code 1827 2025-06-21T00:01:46.184343 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) cbee630c855ceb1de764ac0cdb1d5d781c16cd3a934d67166adc235c98f363d7
211 tests/markdown/__init__.py __init__.py source_code 0 2025-06-21T00:01:46.196570 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
212 tests/markdown/test_entity_parser.py test_entity_parser.py source_code 8669 2025-06-21T00:01:46.206579 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 496fdab6fe164634f8223f827585a4c0dedd30ccea7b0b69dc79004ad0996de2
213 tests/markdown/test_markdown_plugins.py test_markdown_plugins.py source_code 5139 2025-06-21T00:01:46.200759 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 9624d73462aadcd47fb0edd0d87880959a8982eecfa39529f21e3b292c34cb83
214 tests/markdown/test_markdown_processor.py test_markdown_processor.py source_code 5728 2025-06-21T00:01:46.194175 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 35042aa57dd2aeed848c624aa7f69decfb18e83c3b217eca1fdacfc4a10831db
215 tests/markdown/test_observation_edge_cases.py test_observation_edge_cases.py source_code 4295 2025-06-21T00:01:46.209598 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 6f1ab7684bfa8b02af0e0c42b3d7aed91fcd0b1c6288cbc9b6d6943c166979fa
216 tests/markdown/test_parser_edge_cases.py test_parser_edge_cases.py source_code 5142 2025-06-21T00:01:46.213291 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) d511d7d1187e3c1d3a8d3e0ab9efc9e68e84115c46811e7a9dc21eca9a87d520
217 tests/markdown/test_relation_edge_cases.py test_relation_edge_cases.py source_code 3952 2025-06-21T00:01:46.203846 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) f8e2016308f84139342cc37ec6e389ffe52d7eb46f998ee327c7f0d5bba3ad9b
218 tests/markdown/test_task_detection.py test_task_detection.py source_code 401 2025-06-21T00:01:46.191444 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 37fe91a689742033a0eb76723557e31c2a85e591133383b3887572cb714db2a5
219 tests/mcp/conftest.py conftest.py source_code 1761 2025-07-08T20:39:18.351554 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 350b2ca42c52ef76e002deddb65fc0eccb9f8989bd26078ea1c100e3b93bae3f
220 tests/mcp/test_prompts.py test_prompts.py source_code 5989 2025-07-01T08:33:22.093691 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) e3b2ee5b4bc120b5043225ebdbf73eadd71be9c3d9d8e1fdd66a24a8ff11a780
221 tests/mcp/test_resource_project_info.py test_resource_project_info.py source_code 4885 2025-07-01T08:33:22.094085 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6a043736789b239c8d4ae00d1a5332a0fc3bed4038fe3388970ba97dc7431a93
222 tests/mcp/test_resources.py test_resources.py source_code 549 2025-07-01T08:33:22.094481 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 77a6396b9b8a9d3b3d202e438243ca6178d87fd7680c9fc063ee013f2042aa4c
223 tests/mcp/test_tool_build_context.py test_tool_build_context.py source_code 3714 2025-07-01T08:33:22.094661 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 5f0a39fdb652605335ed54aa91fe994262441bb7ba46f381c46ebca882fc23f2
224 tests/mcp/test_tool_canvas.py test_tool_canvas.py source_code 7997 2025-07-01T08:33:22.094982 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) c517bb1f9edc77b76fbeb483dfcb2dd57de9f0b80a10738708bc90cbe388dc9e
225 tests/mcp/test_tool_delete_note.py test_tool_delete_note.py source_code 4208 2025-07-01T08:33:22.095578 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 4cbf3ba4313438651b0608d5b25faf1400f7bd6a78af630351aecd4457800895
226 tests/mcp/test_tool_edit_note.py test_tool_edit_note.py source_code 13503 2025-07-03T18:43:10.084890 Drew Cain 2 Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) dd441969aa54b3739f2d9202d3feaf46aee807ec51e01a13ef41eda52743944f
227 tests/mcp/test_tool_list_directory.py test_tool_list_directory.py source_code 7730 2025-07-01T08:33:22.096737 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) cfbb5c06bcfe74ea60f31051eb9b03ac80b86232b053c188bf3b172132f7c0f5
228 tests/mcp/test_tool_move_note.py test_tool_move_note.py source_code 25851 2025-07-30T09:39:45.727347 jope-bm 2 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co) c9f95158503fa42e37532aaa35027538fa49cea718d059d570cf547d31f01043
229 tests/mcp/test_tool_read_content.py test_tool_read_content.py source_code 19442 2025-07-30T09:39:45.727526 jope-bm 1 jope-bm (joe@basicmemory.com) 1817e32f997b06dbdabf02864d0c70584c1cd80bf1fae8dfbef649de79434bc5
230 tests/mcp/test_tool_read_note.py test_tool_read_note.py source_code 22991 2025-07-30T09:39:45.727746 jope-bm 4 jope-bm (joe@basicmemory.com); Paul Hernandez (paul@basicmachines.co); Amadeusz Wieczorek (git@amadeusw.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 4e13ea9ff977525aa62b6a8c22505ffe989329dc3e7d07b71e044cf43ef3ddfd
231 tests/mcp/test_tool_recent_activity.py test_tool_recent_activity.py source_code 3983 2025-07-01T08:33:22.098561 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 45414152cd45334b84df56f653146b7bd2939907548e7245a37207fdccfc4c87
232 tests/mcp/test_tool_resource.py test_tool_resource.py source_code 6782 2025-07-01T08:33:22.098948 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 3916ae1c4d458eee53d2c570b9bbb708ecd617de5a145c3a7552389954bc9195
233 tests/mcp/test_tool_search.py test_tool_search.py source_code 10959 2025-07-03T18:43:10.085177 Drew Cain 3 Drew Cain (groksrc@gmail.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) 07839cf8e89d74ba05ec1dc55fa842444c1f0cc3d1587823b0ac3458ad89e87c
234 tests/mcp/test_tool_sync_status.py test_tool_sync_status.py source_code 6331 2025-07-01T08:33:22.100021 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 3f3566df5b9f3c66bb9b3bd77d4c05825480d0e85015305cb1b7ab9429c7a7ec
235 tests/mcp/test_tool_utils.py test_tool_utils.py source_code 9228 2025-07-01T08:33:22.100561 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) b50789f7a78b0169f52dc8bd16e262510ed4a0ac130fce698d4be84af1fe2d31
236 tests/mcp/test_tool_view_note.py test_tool_view_note.py source_code 10004 2025-07-01T08:33:22.101164 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 385535ce791c3ab55db827cbcf27ea25ae85faf3a027f52df4eea052cede7f36
237 tests/mcp/test_tool_write_note.py test_tool_write_note.py source_code 35136 2025-07-30T09:39:45.728137 jope-bm 3 jope-bm (joe@basicmemory.com); Drew Cain (groksrc@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 45172de20cfc4f4436287573b4f5a94b8b616a360647af67ba1c6c343ccd41b8
238 tests/repository/test_entity_repository.py test_entity_repository.py source_code 15498 2025-06-21T00:01:46.176265 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) 8f5d1ff6cff7b640993ff73f463b1e7801a4dd69640fc55fa23dc58b865e9943
239 tests/repository/test_entity_repository_upsert.py test_entity_repository_upsert.py source_code 16983 2025-07-03T18:43:10.085482 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com) ee0ecd6f4ff8b31838bd6de6fb1018af437e02813d8315c06f4e20a6b78a0572
240 tests/repository/test_observation_repository.py test_observation_repository.py source_code 11786 2025-06-21T00:01:46.158862 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) 3006d392ad94dcc9b3ae031d63a1af6935624534389dd025de70d4c9e112aa0b
241 tests/repository/test_project_info_repository.py test_project_info_repository.py source_code 1186 2025-06-21T00:01:46.161876 bm-claudeai (AI Assistant) 1 bm-claudeai (AI Assistant) (claude@basicmachines.co) dbce7ae7350403cbc37343c2e3e68e4d2255e247006aaa6c495a7ce8bfddea7d
242 tests/repository/test_project_repository.py test_project_repository.py source_code 10511 2025-07-30T09:39:45.728536 jope-bm 2 jope-bm (joe@basicmemory.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 279a3324635b1b515c18003bd1875cf230fef1192cadef9ca426846783717e75
243 tests/repository/test_relation_repository.py test_relation_repository.py source_code 11980 2025-06-21T00:01:46.167567 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (phernandez@basicmachines.co) fcdbdf40f7b56145b98eedde5ead64ef6187e859adde48cc09f5a97754757428
244 tests/repository/test_repository.py test_repository.py source_code 5996 2025-06-21T00:01:46.152691 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 3a6d4ca0b7a438a30ae504f5eac22874d4e1a937a4246e90efad46c90f0e1c95
245 tests/repository/test_search_repository.py test_search_repository.py source_code 25471 2025-07-03T18:43:10.086041 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 805b5cc55a53ae9d6e86c0de735c7f38ac9fd92c23194b0150fa3e7f5507a146
246 tests/schemas/test_memory_url.py test_memory_url.py source_code 2000 2025-06-21T00:01:46.346515 Paul Hernandez 1 Paul Hernandez (paulmh@gmail.com) 946fa6c48534f41c8b9cc27bbad164d3e3673a79afa84fbb439fae4cd26c504b
247 tests/schemas/test_memory_url_validation.py test_memory_url_validation.py source_code 9338 2025-07-01T08:33:22.103247 Paul Hernandez 1 Paul Hernandez (paul@basicmachines.co) 7054c3a3cdb8e05d2ffd254ed298908ff3d48a0a3927d960975eae1ab9beeb38
248 tests/schemas/test_schemas.py test_schemas.py source_code 16880 2025-07-03T18:43:10.086337 Drew Cain 3 Drew Cain (groksrc@gmail.com); Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 2c746a00c340f063c160ace67569955a300a294040949695c79fd4ae124fb611
249 tests/schemas/test_search.py test_search.py source_code 3264 2025-06-21T00:01:46.343685 github-actions[bot] (AI Assistant) 2 github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com); Paul Hernandez (60959+phernandez@users.noreply.github.com) 7437775d1b4a61a438cbabe3dbb1becd2adc4dbcc0c24595830c0843fa7a3166
250 tests/services/test_context_service.py test_context_service.py source_code 8833 2025-06-21T00:01:46.446200 bm-claudeai (AI Assistant) 2 bm-claudeai (AI Assistant) (claude@basicmachines.co); Paul Hernandez (paulmh@gmail.com) ab49d21a05d5364e7e8a2f00b07aabe1109b7417698e0acd92792e2cb5f22732
251 tests/services/test_directory_service.py test_directory_service.py source_code 6854 2025-06-21T00:01:46.457258 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) a0c765fb6bd1a75b6ba4592b1df344c48273b6ab03ba6bd15d2b5e840eb2d521
252 tests/services/test_entity_service.py test_entity_service.py source_code 59278 2025-07-03T18:43:10.086674 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) d7d495756faa10b415d705cf26b69d2efbc5f190a834b6767386ff296629ea71
253 tests/services/test_file_service.py test_file_service.py source_code 5243 2025-06-21T00:01:46.460141 Paul Hernandez 1 Paul Hernandez (phernandez@basicmachines.co) 83e6e9a7584b7baeccbc84865d8c1d3e2a0223e7d2b9c066d3799c289196ab7d
254 tests/services/test_initialization.py test_initialization.py source_code 6939 2025-07-08T20:39:18.351946 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) ebb8d11ff37dcb36c4844c68f873c8e0e8aa6ab1b82abb22ee96c4233e6ab287
255 tests/services/test_link_resolver.py test_link_resolver.py source_code 13618 2025-07-01T08:33:22.105766 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) c9fc56d0c3572770e95ca6175c213ed8b44c2a6ddddc298b5662cc2d8f41014e
256 tests/services/test_project_service.py test_project_service.py source_code 28152 2025-07-30T09:39:45.728920 jope-bm 3 jope-bm (joe@basicmemory.com); Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 430868ed796518937a3b0bed4d8c33be53264a6100d61d54b0745bb2b58bf29a
257 tests/services/test_project_service_operations.py test_project_service_operations.py source_code 4679 2025-06-21T00:01:46.442141 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) d4103b6c5fff7e74ba84b86033537b8522aa60fc75aec4974d52e1dfec68b1b4
258 tests/services/test_search_service.py test_search_service.py source_code 25299 2025-06-21T00:01:46.439133 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) b3e58258db2b727ab8a1792e52fcb038d3a0779bcd1b70f2d295a9d06e5e2513
259 tests/services/test_sync_status_service.py test_sync_status_service.py source_code 9926 2025-07-03T18:43:10.087266 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 00245ca356e33fc7afcd2d42cb5ec082038c95383d738067821cf1e8d23c9a4b
260 tests/sync/test_sync_service.py test_sync_service.py source_code 39659 2025-07-01T08:33:22.107088 Paul Hernandez 2 Paul Hernandez (paul@basicmachines.co); bm-claudeai (AI Assistant) (claude@basicmachines.co) 0609aee9906549f27fc4289b5ac2d46a50e53e12a0615a2701f17fbe9a74e169
261 tests/sync/test_sync_wikilink_issue.py test_sync_wikilink_issue.py source_code 1959 2025-06-21T00:01:46.420370 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) db4b98dd7008a1a26b0fd02b9a2d03bca950b281efc19d98a91b017c500b23a5
262 tests/sync/test_tmp_files.py test_tmp_files.py source_code 5930 2025-06-21T00:01:46.424277 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) cead95a07b29f6c1ab933abe67f9648122f5f33d33753c8e4ab4934af96cbc25
263 tests/sync/test_watch_service.py test_watch_service.py source_code 14076 2025-06-26T09:28:51.860679 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) e324a4127f8bfe59798f3a08125488c701e88239b7482d87a0394b91c55b893e
264 tests/sync/test_watch_service_edge_cases.py test_watch_service_edge_cases.py source_code 2169 2025-06-21T00:01:46.409830 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) b87c5015330e9b551630aab7ef95ac1e7663f0ce46e912d8155b319e139559aa
265 tests/test_config.py test_config.py source_code 3680 2025-07-03T18:43:10.087558 Paul Hernandez 3 Paul Hernandez (paul@basicmachines.co); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) 6a246431cff739274efda895dfa28f38710289a73e645312f5fa05fce0885ec8
266 tests/test_db_migration_deduplication.py test_db_migration_deduplication.py source_code 6166 2025-07-08T20:39:18.352690 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com) ab768e061531ca2edcd598c101b5d884e549f90c7a4b305a23713a199d263fbb
267 tests/utils/test_file_utils.py test_file_utils.py source_code 6199 2025-06-21T00:01:46.219673 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 4615bb20b92b8a79d9e086301f852b34c3360f17ecc209920d68528268ddeed4
268 tests/utils/test_parse_tags.py test_parse_tags.py source_code 1691 2025-06-21T00:01:46.223400 Paul Hernandez 2 Paul Hernandez (60959+phernandez@users.noreply.github.com); github-actions[bot] (AI Assistant) (41898282+github-actions[bot]@users.noreply.github.com) b83afdf18dac8b3411bd0fab1301683217d3d1abceaf2d22c09e79c709efd8eb
269 tests/utils/test_permalink_formatting.py test_permalink_formatting.py source_code 4193 2025-06-21T00:01:46.225738 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) d998a4ca846c78679728a918b6f71df67c30b6961ede3cffa609f2cf6ad160eb
270 tests/utils/test_utf8_handling.py test_utf8_handling.py source_code 5791 2025-06-21T00:01:46.228483 Paul Hernandez 1 Paul Hernandez (60959+phernandez@users.noreply.github.com) 50ec792e572af51cd2d996d4ddacbcfd3bbd79aa928818a90c42117451ea829f
271 tests/utils/test_validate_project_path.py test_validate_project_path.py source_code 15346 2025-07-30T09:39:45.729224 jope-bm 1 jope-bm (joe@basicmemory.com) 27a4bf2a9d277e6cd773619e5241b20175d211ec60c284361bec404fd99143f3
272 uv.lock uv.lock other 232710 2025-07-30T09:39:36.619155 Paul Hernandez 3 Paul Hernandez (60959+phernandez@users.noreply.github.com); Drew Cain (groksrc@users.noreply.github.com); bm-claudeai (AI Assistant) (claude@basicmachines.co) caa1e3f6e66464b884a78cab77df2fe303fa3c0dc573be0196474b6d3c150581
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+294
View File
@@ -0,0 +1,294 @@
# Basic Memory - Legal File Inventory
**Generated:** 2025-07-29T16:10:54.745774
**Repository:** /Users/phernandez/dev/basicmachines/basic-memory
## Summary Statistics
- **Total Files:** 255
- **Total Contributors:** 12
### Files by Category
- **Build/Deployment:** 2 files
- **Configuration:** 6 files
- **Database/Migration:** 1 files
- **Documentation:** 6 files
- **Legal/License:** 3 files
- **Other:** 13 files
- **Source Code:** 222 files
- **Templates/Resources:** 2 files
### Top Contributors by Files Modified
- **phernandez:** 190 files
- **Paul Hernandez:** 181 files
- **bm-claudeai:** 135 files
- **Drew Cain:** 24 files
- **github-actions[bot]:** 20 files
- **Amadeusz Wieczorek:** 2 files
- **semantic-release:** 1 files
- **Jason Noble:** 1 files
- **Marc Baiza:** 1 files
- **Matias Forbord:** 1 files
## Detailed File Inventory
| File Path | Category | Size (bytes) | Primary Author | Contributors |
|-----------|----------|--------------|----------------|-------------|
| .claude/commands/release/beta.md | Other | 3103 | phernandez | phernandez |
| .claude/commands/release/changelog.md | Documentation | 4648 | phernandez | phernandez |
| .claude/commands/release/release-check.md | Other | 3344 | phernandez | phernandez |
| .claude/commands/release/release.md | Other | 2925 | phernandez | phernandez |
| .claude/commands/test-live.md | Other | 18720 | phernandez | phernandez |
| .claude/settings.local.json | Configuration | 3122 | Unknown | |
| .dockerignore | Other | 623 | Paul Hernandez | Paul Hernandez |
| .python-version | Other | 5 | phernandez | phernandez |
| CHANGELOG.md | Documentation | 53647 | semantic-release | semantic-release, phernandez |
| CITATION.cff | Legal/License | 302 | phernandez | phernandez |
| CLA.md | Legal/License | 1342 | phernandez | phernandez |
| CLAUDE.md | Other | 12310 | Paul Hernandez | Paul Hernandez, phernandez, Ikko Eltociear Ashimin... |
| CODE_OF_CONDUCT.md | Other | 509 | phernandez | phernandez |
| CONTRIBUTING.md | Documentation | 7241 | phernandez | phernandez, Paul Hernandez |
| Dockerfile | Build/Deployment | 856 | Paul Hernandez | Paul Hernandez, bm-claudeai |
| LICENSE | Legal/License | 34523 | Paul Hernandez | Paul Hernandez |
| README.md | Documentation | 16261 | phernandez | phernandez, bm-claudeai, Paul Hernandez, Jason Nob... |
| SECURITY.md | Other | 303 | Paul Hernandez | Paul Hernandez |
| docker-compose.yml | Configuration | 2534 | Paul Hernandez | Paul Hernandez, phernandez |
| docs/AI Assistant Guide.md | Documentation | 16423 | Paul Hernandez | Paul Hernandez, phernandez |
| docs/Docker.md | Documentation | 9088 | Paul Hernandez | Paul Hernandez, phernandez |
| justfile | Build/Deployment | 5371 | phernandez | phernandez, Drew Cain |
| legal_file_inventory.py | Source Code | 17407 | Unknown | |
| llms-install.md | Other | 2307 | phernandez | phernandez |
| memory.json | Configuration | 90075 | phernandez | phernandez |
| pyproject.toml | Configuration | 3279 | phernandez | phernandez, Paul Hernandez, bm-claudeai, Drew Cain... |
| smithery.yaml | Configuration | 433 | bm-claudeai | bm-claudeai |
| src/basic_memory/__init__.py | Source Code | 256 | phernandez | phernandez |
| src/basic_memory/alembic/alembic.ini | Configuration | 3733 | phernandez | phernandez |
| src/basic_memory/alembic/migrations.py | Source Code | 718 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/alembic/script.py.mako | Database/Migration | 635 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py | Source Code | 4363 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py | Source Code | 1840 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py | Source Code | 4379 | bm-claudeai | bm-claudeai |
| src/basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py | Source Code | 4210 | phernandez | phernandez |
| src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py | Source Code | 1428 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py | Source Code | 3679 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/api/__init__.py | Source Code | 72 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/api/app.py | Source Code | 2897 | bm-claudeai | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/api/routers/__init__.py | Source Code | 398 | phernandez | phernandez, bm-claudeai |
| src/basic_memory/api/routers/directory_router.py | Source Code | 2054 | Paul Hernandez | bm-claudeai, Paul Hernandez |
| src/basic_memory/api/routers/importer_router.py | Source Code | 4513 | bm-claudeai | bm-claudeai |
| src/basic_memory/api/routers/knowledge_router.py | Source Code | 9738 | Paul Hernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/api/routers/management_router.py | Source Code | 2730 | bm-claudeai | bm-claudeai, phernandez |
| src/basic_memory/api/routers/memory_router.py | Source Code | 3003 | phernandez | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/api/routers/project_router.py | Source Code | 8239 | bm-claudeai | bm-claudeai, phernandez, Paul Hernandez |
| src/basic_memory/api/routers/prompt_router.py | Source Code | 9948 | bm-claudeai | bm-claudeai, phernandez |
| src/basic_memory/api/routers/resource_router.py | Source Code | 8058 | Paul Hernandez | phernandez, Paul Hernandez |
| src/basic_memory/api/routers/search_router.py | Source Code | 1226 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/api/routers/utils.py | Source Code | 5159 | bm-claudeai | bm-claudeai, Drew Cain |
| src/basic_memory/api/template_loader.py | Source Code | 8607 | bm-claudeai | bm-claudeai |
| src/basic_memory/cli/__init__.py | Source Code | 33 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/cli/app.py | Source Code | 2268 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/cli/commands/__init__.py | Source Code | 393 | Paul Hernandez | Paul Hernandez, phernandez |
| src/basic_memory/cli/commands/db.py | Source Code | 1480 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/cli/commands/import_chatgpt.py | Source Code | 2856 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| src/basic_memory/cli/commands/import_claude_conversations.py | Source Code | 2946 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| src/basic_memory/cli/commands/import_claude_projects.py | Source Code | 2891 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| src/basic_memory/cli/commands/import_memory_json.py | Source Code | 2947 | Paul Hernandez | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/cli/commands/mcp.py | Source Code | 2593 | phernandez | bm-claudeai, phernandez, Paul Hernandez |
| src/basic_memory/cli/commands/project.py | Source Code | 12252 | phernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/cli/commands/status.py | Source Code | 5809 | phernandez | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/cli/commands/sync.py | Source Code | 8213 | phernandez | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/cli/commands/tool.py | Source Code | 9540 | phernandez | Paul Hernandez, phernandez, github-actions[bot] |
| src/basic_memory/cli/main.py | Source Code | 465 | Paul Hernandez | Paul Hernandez, phernandez |
| src/basic_memory/config.py | Source Code | 12620 | Paul Hernandez | phernandez, Paul Hernandez, bm-claudeai, Drew Cain... |
| src/basic_memory/db.py | Source Code | 7428 | phernandez | phernandez, Paul Hernandez, Drew Cain, bm-claudeai... |
| src/basic_memory/deps.py | Source Code | 12144 | bm-claudeai | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/file_utils.py | Source Code | 6700 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/importers/__init__.py | Source Code | 795 | bm-claudeai | bm-claudeai |
| src/basic_memory/importers/base.py | Source Code | 2531 | bm-claudeai | bm-claudeai |
| src/basic_memory/importers/chatgpt_importer.py | Source Code | 7372 | bm-claudeai | bm-claudeai |
| src/basic_memory/importers/claude_conversations_importer.py | Source Code | 5725 | bm-claudeai | bm-claudeai |
| src/basic_memory/importers/claude_projects_importer.py | Source Code | 5389 | bm-claudeai | bm-claudeai |
| src/basic_memory/importers/memory_json_importer.py | Source Code | 3986 | bm-claudeai | bm-claudeai, phernandez |
| src/basic_memory/importers/utils.py | Source Code | 1792 | bm-claudeai | bm-claudeai |
| src/basic_memory/markdown/__init__.py | Source Code | 501 | phernandez | phernandez |
| src/basic_memory/markdown/entity_parser.py | Source Code | 4516 | phernandez | phernandez, Paul Hernandez, github-actions[bot] |
| src/basic_memory/markdown/markdown_processor.py | Source Code | 4968 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/markdown/plugins.py | Source Code | 6639 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/markdown/schemas.py | Source Code | 1896 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/markdown/utils.py | Source Code | 3410 | Paul Hernandez | Paul Hernandez, phernandez |
| src/basic_memory/mcp/__init__.py | Source Code | 35 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/mcp/async_client.py | Source Code | 925 | Paul Hernandez | phernandez, Paul Hernandez |
| src/basic_memory/mcp/project_session.py | Source Code | 4203 | Paul Hernandez | Paul Hernandez, phernandez |
| src/basic_memory/mcp/prompts/__init__.py | Source Code | 615 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/mcp/prompts/ai_assistant_guide.py | Source Code | 821 | Paul Hernandez | Paul Hernandez, phernandez |
| src/basic_memory/mcp/prompts/continue_conversation.py | Source Code | 1998 | Paul Hernandez | Paul Hernandez, github-actions[bot], bm-claudeai |
| src/basic_memory/mcp/prompts/recent_activity.py | Source Code | 3311 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/mcp/prompts/search.py | Source Code | 1692 | Paul Hernandez | Paul Hernandez, bm-claudeai |
| src/basic_memory/mcp/prompts/utils.py | Source Code | 5431 | Paul Hernandez | Paul Hernandez, bm-claudeai |
| src/basic_memory/mcp/resources/ai_assistant_guide.md | Other | 14777 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/mcp/resources/project_info.py | Source Code | 1906 | Paul Hernandez | Paul Hernandez, bm-claudeai |
| src/basic_memory/mcp/server.py | Source Code | 1248 | Paul Hernandez | bm-claudeai, phernandez, Paul Hernandez |
| src/basic_memory/mcp/tools/__init__.py | Source Code | 1619 | Paul Hernandez | phernandez, Paul Hernandez, Drew Cain |
| src/basic_memory/mcp/tools/auth.py | Source Code | 1231 | phernandez | phernandez |
| src/basic_memory/mcp/tools/canvas.py | Source Code | 3738 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/mcp/tools/delete_note.py | Source Code | 7346 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/mcp/tools/edit_note.py | Source Code | 13570 | Paul Hernandez | Paul Hernandez, phernandez |
| src/basic_memory/mcp/tools/list_directory.py | Source Code | 5236 | Paul Hernandez | Paul Hernandez |
| src/basic_memory/mcp/tools/move_note.py | Source Code | 16708 | phernandez | Paul Hernandez, phernandez |
| src/basic_memory/mcp/tools/project_management.py | Source Code | 12944 | Paul Hernandez | Paul Hernandez, phernandez, Drew Cain |
| src/basic_memory/mcp/tools/read_content.py | Source Code | 8596 | Paul Hernandez | Paul Hernandez, bm-claudeai |
| src/basic_memory/mcp/tools/read_note.py | Source Code | 7614 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez, Amadeusz ... |
| src/basic_memory/mcp/tools/recent_activity.py | Source Code | 4833 | phernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/mcp/tools/search.py | Source Code | 15883 | phernandez | phernandez, github-actions[bot], Paul Hernandez, D... |
| src/basic_memory/mcp/tools/sync_status.py | Source Code | 10517 | phernandez | phernandez |
| src/basic_memory/mcp/tools/utils.py | Source Code | 21426 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/mcp/tools/view_note.py | Source Code | 2499 | phernandez | phernandez |
| src/basic_memory/mcp/tools/write_note.py | Source Code | 6178 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| src/basic_memory/models/__init__.py | Source Code | 333 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/models/base.py | Source Code | 225 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/models/knowledge.py | Source Code | 7087 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/models/project.py | Source Code | 2773 | bm-claudeai | bm-claudeai, phernandez |
| src/basic_memory/models/search.py | Source Code | 1300 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/repository/__init__.py | Source Code | 327 | Paul Hernandez | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/repository/entity_repository.py | Source Code | 10405 | Drew Cain | phernandez, Paul Hernandez, Drew Cain, bm-claudeai... |
| src/basic_memory/repository/observation_repository.py | Source Code | 2943 | phernandez | phernandez, bm-claudeai |
| src/basic_memory/repository/project_info_repository.py | Source Code | 338 | Paul Hernandez | Paul Hernandez, bm-claudeai |
| src/basic_memory/repository/project_repository.py | Source Code | 3159 | bm-claudeai | bm-claudeai |
| src/basic_memory/repository/relation_repository.py | Source Code | 3179 | phernandez | phernandez, bm-claudeai |
| src/basic_memory/repository/repository.py | Source Code | 15224 | phernandez | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/repository/search_repository.py | Source Code | 21936 | phernandez | phernandez, Paul Hernandez, github-actions[bot], b... |
| src/basic_memory/schemas/__init__.py | Source Code | 1674 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/schemas/base.py | Source Code | 6738 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/schemas/delete.py | Source Code | 1180 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/schemas/directory.py | Source Code | 881 | bm-claudeai | bm-claudeai |
| src/basic_memory/schemas/importer.py | Source Code | 663 | bm-claudeai | bm-claudeai |
| src/basic_memory/schemas/memory.py | Source Code | 5833 | phernandez | phernandez, Paul Hernandez, Drew Cain, bm-claudeai... |
| src/basic_memory/schemas/project_info.py | Source Code | 7047 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| src/basic_memory/schemas/prompt.py | Source Code | 3648 | bm-claudeai | bm-claudeai |
| src/basic_memory/schemas/request.py | Source Code | 3760 | Paul Hernandez | phernandez, Paul Hernandez |
| src/basic_memory/schemas/response.py | Source Code | 6450 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/schemas/search.py | Source Code | 3657 | phernandez | phernandez, Paul Hernandez, github-actions[bot], b... |
| src/basic_memory/services/__init__.py | Source Code | 259 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/services/context_service.py | Source Code | 14462 | bm-claudeai | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/services/directory_service.py | Source Code | 6017 | bm-claudeai | bm-claudeai, Paul Hernandez |
| src/basic_memory/services/entity_service.py | Source Code | 30440 | Paul Hernandez | phernandez, Paul Hernandez, bm-claudeai, Drew Cain... |
| src/basic_memory/services/exceptions.py | Source Code | 390 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/services/file_service.py | Source Code | 10059 | Paul Hernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/services/initialization.py | Source Code | 6715 | bm-claudeai | Paul Hernandez, bm-claudeai, Drew Cain, phernandez... |
| src/basic_memory/services/link_resolver.py | Source Code | 4594 | phernandez | phernandez, Paul Hernandez, bm-claudeai, github-ac... |
| src/basic_memory/services/project_service.py | Source Code | 28386 | bm-claudeai | bm-claudeai, Paul Hernandez, phernandez |
| src/basic_memory/services/search_service.py | Source Code | 12782 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| src/basic_memory/services/service.py | Source Code | 336 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/services/sync_status_service.py | Source Code | 7504 | phernandez | phernandez, Paul Hernandez |
| src/basic_memory/sync/__init__.py | Source Code | 156 | Paul Hernandez | Paul Hernandez, phernandez |
| src/basic_memory/sync/background_sync.py | Source Code | 726 | bm-claudeai | bm-claudeai, phernandez |
| src/basic_memory/sync/sync_service.py | Source Code | 23387 | Paul Hernandez | phernandez, Paul Hernandez, bm-claudeai, github-ac... |
| src/basic_memory/sync/watch_service.py | Source Code | 15316 | Paul Hernandez | phernandez, bm-claudeai, Paul Hernandez |
| src/basic_memory/templates/prompts/continue_conversation.hbs | Templates/Resources | 3076 | bm-claudeai | bm-claudeai, Drew Cain |
| src/basic_memory/templates/prompts/search.hbs | Templates/Resources | 3098 | bm-claudeai | bm-claudeai |
| src/basic_memory/utils.py | Source Code | 7657 | phernandez | phernandez, Paul Hernandez, andyxinweiminicloud, D... |
| test-int/conftest.py | Source Code | 8203 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_delete_note_integration.py | Source Code | 13175 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_edit_note_integration.py | Source Code | 19822 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_list_directory_integration.py | Source Code | 14744 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_move_note_integration.py | Source Code | 19825 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_project_management_integration.py | Source Code | 33175 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_project_state_sync_integration.py | Source Code | 7728 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_read_content_integration.py | Source Code | 11187 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_read_note_integration.py | Source Code | 1381 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_search_integration.py | Source Code | 14690 | Paul Hernandez | Paul Hernandez, phernandez |
| test-int/mcp/test_write_note_integration.py | Source Code | 9472 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/Non-MarkdownFileSupport.pdf | Other | 106751 | Unknown | |
| tests/Screenshot.png | Other | 190076 | Unknown | |
| tests/__init__.py | Source Code | 141 | phernandez | phernandez, Paul Hernandez |
| tests/api/conftest.py | Source Code | 1404 | phernandez | phernandez, bm-claudeai, Paul Hernandez |
| tests/api/test_async_client.py | Source Code | 1382 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/api/test_continue_conversation_template.py | Source Code | 5152 | bm-claudeai | bm-claudeai |
| tests/api/test_directory_router.py | Source Code | 9376 | Paul Hernandez | bm-claudeai, Paul Hernandez |
| tests/api/test_importer_router.py | Source Code | 16578 | bm-claudeai | bm-claudeai, Paul Hernandez |
| tests/api/test_knowledge_router.py | Source Code | 45771 | Paul Hernandez | phernandez, bm-claudeai, Paul Hernandez |
| tests/api/test_management_router.py | Source Code | 6220 | bm-claudeai | bm-claudeai |
| tests/api/test_memory_router.py | Source Code | 5555 | phernandez | phernandez, bm-claudeai |
| tests/api/test_project_router.py | Source Code | 5201 | Paul Hernandez | bm-claudeai, Paul Hernandez, phernandez |
| tests/api/test_project_router_operations.py | Source Code | 1812 | bm-claudeai | bm-claudeai, Paul Hernandez |
| tests/api/test_prompt_router.py | Source Code | 5046 | bm-claudeai | bm-claudeai |
| tests/api/test_resource_router.py | Source Code | 13960 | Paul Hernandez | phernandez, Paul Hernandez, bm-claudeai |
| tests/api/test_search_router.py | Source Code | 6409 | phernandez | phernandez, bm-claudeai, Paul Hernandez, github-ac... |
| tests/api/test_search_template.py | Source Code | 5258 | bm-claudeai | bm-claudeai |
| tests/api/test_template_loader.py | Source Code | 8035 | bm-claudeai | bm-claudeai |
| tests/api/test_template_loader_helpers.py | Source Code | 7445 | bm-claudeai | bm-claudeai |
| tests/cli/conftest.py | Source Code | 1185 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/cli/test_cli_tools.py | Source Code | 13528 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| tests/cli/test_import_chatgpt.py | Source Code | 6719 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| tests/cli/test_import_claude_conversations.py | Source Code | 5028 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| tests/cli/test_import_claude_projects.py | Source Code | 4585 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| tests/cli/test_import_memory_json.py | Source Code | 3532 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| tests/cli/test_project_commands.py | Source Code | 5143 | bm-claudeai | bm-claudeai, Paul Hernandez, phernandez |
| tests/cli/test_project_info.py | Source Code | 3874 | phernandez | Paul Hernandez, phernandez |
| tests/cli/test_status.py | Source Code | 4160 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| tests/cli/test_sync.py | Source Code | 3864 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| tests/cli/test_version.py | Source Code | 289 | Paul Hernandez | Paul Hernandez |
| tests/conftest.py | Source Code | 15286 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| tests/importers/test_importer_base.py | Source Code | 4329 | bm-claudeai | bm-claudeai |
| tests/importers/test_importer_utils.py | Source Code | 1827 | bm-claudeai | bm-claudeai |
| tests/markdown/__init__.py | Source Code | 0 | Unknown | |
| tests/markdown/test_entity_parser.py | Source Code | 8669 | phernandez | phernandez, Paul Hernandez, github-actions[bot] |
| tests/markdown/test_markdown_plugins.py | Source Code | 5139 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/markdown/test_markdown_processor.py | Source Code | 5728 | phernandez | phernandez, Paul Hernandez |
| tests/markdown/test_observation_edge_cases.py | Source Code | 4295 | phernandez | phernandez |
| tests/markdown/test_parser_edge_cases.py | Source Code | 5142 | phernandez | phernandez |
| tests/markdown/test_relation_edge_cases.py | Source Code | 3952 | phernandez | phernandez |
| tests/markdown/test_task_detection.py | Source Code | 401 | Paul Hernandez | Paul Hernandez |
| tests/mcp/conftest.py | Source Code | 1761 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| tests/mcp/test_prompts.py | Source Code | 5989 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| tests/mcp/test_resource_project_info.py | Source Code | 4885 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| tests/mcp/test_resources.py | Source Code | 549 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/mcp/test_tool_canvas.py | Source Code | 7997 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/mcp/test_tool_delete_note.py | Source Code | 4208 | phernandez | phernandez |
| tests/mcp/test_tool_edit_note.py | Source Code | 13503 | Paul Hernandez | Paul Hernandez, phernandez, Drew Cain |
| tests/mcp/test_tool_list_directory.py | Source Code | 7730 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/mcp/test_tool_move_note.py | Source Code | 16346 | Paul Hernandez | Paul Hernandez, phernandez |
| tests/mcp/test_tool_read_note.py | Source Code | 9741 | Paul Hernandez | Paul Hernandez, phernandez, github-actions[bot], A... |
| tests/mcp/test_tool_recent_activity.py | Source Code | 3983 | phernandez | phernandez, bm-claudeai |
| tests/mcp/test_tool_resource.py | Source Code | 6782 | Paul Hernandez | Paul Hernandez, phernandez, github-actions[bot] |
| tests/mcp/test_tool_search.py | Source Code | 10959 | phernandez | Paul Hernandez, phernandez, github-actions[bot], D... |
| tests/mcp/test_tool_sync_status.py | Source Code | 6331 | phernandez | phernandez |
| tests/mcp/test_tool_utils.py | Source Code | 9228 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| tests/mcp/test_tool_view_note.py | Source Code | 10004 | phernandez | phernandez |
| tests/mcp/test_tool_write_note.py | Source Code | 19537 | Paul Hernandez | phernandez, Paul Hernandez, Drew Cain |
| tests/repository/test_entity_repository.py | Source Code | 15498 | phernandez | phernandez, bm-claudeai, Paul Hernandez |
| tests/repository/test_entity_repository_upsert.py | Source Code | 16983 | Drew Cain | Drew Cain, phernandez |
| tests/repository/test_observation_repository.py | Source Code | 11786 | phernandez | phernandez, bm-claudeai |
| tests/repository/test_project_info_repository.py | Source Code | 1186 | bm-claudeai | bm-claudeai |
| tests/repository/test_project_repository.py | Source Code | 9455 | bm-claudeai | bm-claudeai |
| tests/repository/test_relation_repository.py | Source Code | 11980 | phernandez | phernandez, bm-claudeai |
| tests/repository/test_repository.py | Source Code | 5996 | phernandez | phernandez, Paul Hernandez |
| tests/repository/test_search_repository.py | Source Code | 25471 | bm-claudeai | bm-claudeai, phernandez, Paul Hernandez |
| tests/schemas/test_memory_url.py | Source Code | 2000 | phernandez | phernandez, Paul Hernandez |
| tests/schemas/test_memory_url_validation.py | Source Code | 9338 | phernandez | phernandez |
| tests/schemas/test_schemas.py | Source Code | 16880 | phernandez | phernandez, Paul Hernandez, bm-claudeai, Drew Cain... |
| tests/schemas/test_search.py | Source Code | 3264 | phernandez | phernandez, Paul Hernandez, github-actions[bot] |
| tests/services/test_context_service.py | Source Code | 8833 | phernandez | phernandez, bm-claudeai, Paul Hernandez |
| tests/services/test_directory_service.py | Source Code | 6854 | Paul Hernandez | bm-claudeai, Paul Hernandez |
| tests/services/test_entity_service.py | Source Code | 59278 | Paul Hernandez | phernandez, Paul Hernandez, bm-claudeai, Drew Cain... |
| tests/services/test_file_service.py | Source Code | 5243 | phernandez | phernandez |
| tests/services/test_initialization.py | Source Code | 6939 | bm-claudeai | Paul Hernandez, bm-claudeai, phernandez |
| tests/services/test_link_resolver.py | Source Code | 13618 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| tests/services/test_project_service.py | Source Code | 23576 | phernandez | bm-claudeai, Paul Hernandez, phernandez |
| tests/services/test_project_service_operations.py | Source Code | 4679 | bm-claudeai | bm-claudeai, Paul Hernandez |
| tests/services/test_search_service.py | Source Code | 25299 | Paul Hernandez | phernandez, Paul Hernandez, github-actions[bot] |
| tests/services/test_sync_status_service.py | Source Code | 9926 | phernandez | phernandez, Paul Hernandez |
| tests/sync/test_sync_service.py | Source Code | 39659 | phernandez | phernandez, Paul Hernandez, bm-claudeai |
| tests/sync/test_sync_wikilink_issue.py | Source Code | 1959 | github-actions[bot] | github-actions[bot], Paul Hernandez |
| tests/sync/test_tmp_files.py | Source Code | 5930 | Paul Hernandez | Paul Hernandez, bm-claudeai, phernandez |
| tests/sync/test_watch_service.py | Source Code | 14076 | Paul Hernandez | Paul Hernandez, phernandez, bm-claudeai |
| tests/sync/test_watch_service_edge_cases.py | Source Code | 2169 | Paul Hernandez | Paul Hernandez, bm-claudeai |
| tests/test_config.py | Source Code | 3680 | Drew Cain | Drew Cain |
| tests/test_db_migration_deduplication.py | Source Code | 6166 | Paul Hernandez | Paul Hernandez, Drew Cain, phernandez |
| tests/utils/test_file_utils.py | Source Code | 6199 | phernandez | phernandez, Paul Hernandez |
| tests/utils/test_parse_tags.py | Source Code | 1691 | github-actions[bot] | github-actions[bot], Paul Hernandez |
| tests/utils/test_permalink_formatting.py | Source Code | 4193 | phernandez | phernandez, Paul Hernandez |
| tests/utils/test_utf8_handling.py | Source Code | 5791 | phernandez | phernandez, Paul Hernandez |
+2 -2
View File
@@ -30,7 +30,7 @@ dependencies = [
"alembic>=1.14.1",
"pillow>=11.1.0",
"pybars3>=0.9.7",
"fastmcp>=2.3.4,<2.10.0",
"fastmcp==2.10.2",
"pyjwt>=2.10.1",
"python-dotenv>=1.1.0",
"pytest-aio>=1.9.0",
@@ -52,7 +52,7 @@ build-backend = "hatchling.build"
[tool.pytest.ini_options]
pythonpath = ["src", "tests"]
addopts = "--cov=basic_memory --cov-report term-missing -ra -q"
addopts = "--cov=basic_memory --cov-report term-missing"
testpaths = ["tests"]
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "function"
+483
View File
@@ -0,0 +1,483 @@
#!/usr/bin/env python3
"""
Legal File Inventory Generator for Basic Memory
Generates comprehensive file inventory with contributor information
for legal documentation, copyright assignments, and due diligence.
Usage:
python scripts/generate_legal_inventory.py [options]
Output formats:
- CSV: Detailed spreadsheet for analysis
- JSON: Structured data for integration
- Markdown: Human-readable legal report
"""
import argparse
import csv
import hashlib
import json
import subprocess
from collections import defaultdict
from datetime import datetime
from pathlib import Path
from typing import Dict, List
class LegalInventoryGenerator:
"""Generate comprehensive file inventory for legal documentation."""
# Files to exclude from legal inventory
EXCLUDED_PATTERNS = {
# Generated/compiled files
'*.pyc', '*.pyo', '*.pyd', '__pycache__',
'*.so', '*.dylib', '*.dll',
# Build/cache directories
'build/', 'dist/', '.eggs/', '*.egg-info/',
'.coverage', '.pytest_cache/', '.mypy_cache/',
'.ruff_cache/', '.tox/', 'venv/', '.venv/', 'env/', '.env/',
'node_modules/', '.npm/', '.yarn/',
# IDE and editor files
'.vscode/', '.idea/', '*.swp', '*.swo', '*~',
'.DS_Store', 'Thumbs.db',
# Version control
'.git/', '.gitignore',
# OS generated
'desktop.ini', '*.tmp', '*.temp'
}
# File categories for legal classification
FILE_CATEGORIES = {
'source_code': ['.py', '.pyx', '.pyi'],
'documentation': ['.md', '.rst', '.txt'],
'configuration': ['.toml', '.yaml', '.yml', '.json', '.ini', '.cfg'],
'legal': ['LICENSE', 'COPYING', 'COPYRIGHT', '.md'],
'build_deployment': ['Dockerfile', 'Makefile', 'justfile', '.sh'],
'database': ['.sql', '.sqlite', '.db'],
'templates': ['.j2', '.jinja2', '.hbs', '.handlebars'],
'data': ['.csv', '.json', '.xml'],
'other': [] # Catch-all for uncategorized files
}
def __init__(self, repo_path: str = "."):
"""Initialize generator with repository path."""
self.repo_path = Path(repo_path).resolve()
self.file_inventory: List[Dict] = []
self.contributors: Dict[str, Dict] = defaultdict(lambda: {
'email': '', 'commits': 0, 'lines_added': 0, 'files': set()
})
def should_exclude_file(self, file_path: Path) -> bool:
"""Check if file should be excluded from inventory."""
# Check if file is tracked by git (more efficient than check-ignore)
try:
rel_path = str(file_path.relative_to(self.repo_path))
result = subprocess.run([
'git', 'ls-files', '--error-unmatch', rel_path
], capture_output=True, cwd=self.repo_path)
# If git ls-files returns non-zero, file is not tracked (likely ignored)
if result.returncode != 0:
return True
except Exception:
# Fallback to manual exclusion patterns if git fails
pass
# Additional manual exclusions for safety
file_str = str(file_path.relative_to(self.repo_path))
for pattern in self.EXCLUDED_PATTERNS:
if pattern.endswith('/'):
if any(part == pattern[:-1] for part in file_path.parts):
return True
elif '*' in pattern:
import fnmatch
if fnmatch.fnmatch(file_str, pattern):
return True
else:
if file_path.name == pattern or file_str == pattern:
return True
return False
def categorize_file(self, file_path: Path) -> str:
"""Categorize file based on extension and name."""
suffix = file_path.suffix.lower()
name = file_path.name.upper()
# Check legal files by name first
if any(legal in name for legal in ['LICENSE', 'COPYING', 'COPYRIGHT', 'CLA']):
return 'legal'
# Check by extension
for category, extensions in self.FILE_CATEGORIES.items():
if suffix in extensions:
return category
return 'other'
def get_file_hash(self, file_path: Path) -> str:
"""Generate SHA-256 hash of file content."""
try:
with open(file_path, 'rb') as f:
return hashlib.sha256(f.read()).hexdigest()
except (IOError, OSError):
return "ERROR_READING_FILE"
def get_git_contributors(self, file_path: Path) -> List[Dict]:
"""Get contributor information for a specific file."""
try:
rel_path = file_path.relative_to(self.repo_path)
# Get contributors with line counts
result = subprocess.run([
'git', 'log', '--follow', '--pretty=format:%an|%ae|%ad|%H',
'--date=short', '--', str(rel_path)
], capture_output=True, text=True, cwd=self.repo_path)
if result.returncode != 0:
return []
contributors = []
seen = set()
for line in result.stdout.strip().split('\n'):
if not line:
continue
parts = line.split('|')
if len(parts) >= 4:
name, email, date, commit_hash = parts[:4]
# Normalize author names/emails
normalized_name = self.normalize_author_name(name, email)
if normalized_name not in seen:
contributors.append({
'name': normalized_name,
'email': email,
'first_contribution': date,
'commit_hash': commit_hash
})
seen.add(normalized_name)
return contributors
except Exception as e:
print(f"Warning: Could not get git info for {file_path}: {e}")
return []
def normalize_author_name(self, name: str, email: str) -> str:
"""Normalize author names to handle multiple emails for same person."""
# Known mappings for Basic Memory team
name_mappings = {
'phernandez': 'Paul Hernandez',
'Paul Hernandez': 'Paul Hernandez',
'drew-cain': 'Drew Cain',
'Drew Cain': 'Drew Cain'
}
# Handle GitHub bot accounts
if 'bot' in name.lower() or 'claude' in name.lower():
return f"{name} (AI Assistant)"
return name_mappings.get(name, name)
def get_file_stats(self, file_path: Path) -> Dict:
"""Get comprehensive file statistics."""
try:
stat = file_path.stat()
rel_path = file_path.relative_to(self.repo_path)
# Basic file info
file_info = {
'path': str(rel_path),
'name': file_path.name,
'size_bytes': stat.st_size,
'modified_time': datetime.fromtimestamp(stat.st_mtime).isoformat(),
'category': self.categorize_file(file_path),
'sha256_hash': self.get_file_hash(file_path)
}
# Git information
contributors = self.get_git_contributors(file_path)
file_info['contributors'] = contributors
file_info['primary_author'] = contributors[0]['name'] if contributors else 'Unknown'
file_info['contributor_count'] = len(contributors)
# Update global contributor stats
for contrib in contributors:
name = contrib['name']
self.contributors[name]['email'] = contrib['email']
self.contributors[name]['files'].add(str(rel_path))
return file_info
except Exception as e:
print(f"Error processing {file_path}: {e}")
return None
def scan_repository(self) -> None:
"""Scan repository and build file inventory."""
print(f"Scanning repository: {self.repo_path}")
# Get all git-tracked files first (much more efficient)
try:
result = subprocess.run([
'git', 'ls-files'
], capture_output=True, text=True, cwd=self.repo_path)
if result.returncode == 0:
tracked_files = [self.repo_path / f for f in result.stdout.strip().split('\n') if f]
print(f"Found {len(tracked_files)} git-tracked files")
for file_path in tracked_files:
if file_path.is_file():
file_info = self.get_file_stats(file_path)
if file_info:
self.file_inventory.append(file_info)
else:
print("Warning: Could not get git tracked files, falling back to directory scan")
self._fallback_scan()
except Exception as e:
print(f"Warning: Git command failed ({e}), falling back to directory scan")
self._fallback_scan()
# Get global git stats
self._get_global_git_stats()
print(f"Processed {len(self.file_inventory)} files")
print(f"Found {len(self.contributors)} contributors")
def _fallback_scan(self) -> None:
"""Fallback directory scan if git commands fail."""
for file_path in self.repo_path.rglob('*'):
if file_path.is_file() and not self.should_exclude_file(file_path):
file_info = self.get_file_stats(file_path)
if file_info:
self.file_inventory.append(file_info)
def _get_global_git_stats(self) -> None:
"""Get global contributor statistics from git."""
try:
# Get commit counts per author
result = subprocess.run([
'git', 'shortlog', '-sn', '--all'
], capture_output=True, text=True, cwd=self.repo_path)
if result.returncode == 0:
for line in result.stdout.strip().split('\n'):
if line.strip():
parts = line.strip().split('\t', 1)
if len(parts) == 2:
count, name = parts
normalized_name = self.normalize_author_name(name, '')
self.contributors[normalized_name]['commits'] = int(count)
except Exception as e:
print(f"Warning: Could not get global git stats: {e}")
def generate_summary(self) -> Dict:
"""Generate inventory summary statistics."""
total_files = len(self.file_inventory)
total_size = sum(f['size_bytes'] for f in self.file_inventory)
# Category breakdown
categories = defaultdict(int)
for file_info in self.file_inventory:
categories[file_info['category']] += 1
# Top contributors
top_contributors = sorted(
self.contributors.items(),
key=lambda x: len(x[1]['files']),
reverse=True
)[:10]
return {
'scan_date': datetime.now().isoformat(),
'repository_path': str(self.repo_path),
'total_files': total_files,
'total_size_bytes': total_size,
'categories': dict(categories),
'contributor_count': len(self.contributors),
'top_contributors': [
{
'name': name,
'file_count': len(stats['files']),
'commit_count': stats['commits'],
'email': stats['email']
}
for name, stats in top_contributors
]
}
def export_csv(self, output_path: str) -> None:
"""Export inventory to CSV format."""
with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = [
'path', 'name', 'category', 'size_bytes', 'modified_time',
'primary_author', 'contributor_count', 'contributors_list',
'sha256_hash'
]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for file_info in sorted(self.file_inventory, key=lambda x: x['path']):
contributors_list = '; '.join([
f"{c['name']} ({c['email']})" for c in file_info['contributors']
])
writer.writerow({
'path': file_info['path'],
'name': file_info['name'],
'category': file_info['category'],
'size_bytes': file_info['size_bytes'],
'modified_time': file_info['modified_time'],
'primary_author': file_info['primary_author'],
'contributor_count': file_info['contributor_count'],
'contributors_list': contributors_list,
'sha256_hash': file_info['sha256_hash']
})
print(f"CSV export saved to: {output_path}")
def export_json(self, output_path: str) -> None:
"""Export inventory to JSON format."""
# Convert sets to lists for JSON serialization
contributors_serializable = {}
for name, stats in self.contributors.items():
contributors_serializable[name] = {
'email': stats['email'],
'commits': stats['commits'],
'lines_added': stats['lines_added'],
'files': list(stats['files'])
}
data = {
'summary': self.generate_summary(),
'files': self.file_inventory,
'contributors': contributors_serializable
}
with open(output_path, 'w', encoding='utf-8') as jsonfile:
json.dump(data, jsonfile, indent=2, ensure_ascii=False)
print(f"JSON export saved to: {output_path}")
def export_markdown(self, output_path: str) -> None:
"""Export inventory to Markdown format for legal documentation."""
summary = self.generate_summary()
with open(output_path, 'w', encoding='utf-8') as mdfile:
mdfile.write("# Basic Memory - Legal File Inventory\n\n")
# Summary section
mdfile.write("## Summary\n\n")
mdfile.write(f"**Scan Date:** {summary['scan_date']}\n")
mdfile.write(f"**Repository:** {summary['repository_path']}\n")
mdfile.write(f"**Total Files:** {summary['total_files']:,}\n")
mdfile.write(f"**Total Size:** {summary['total_size_bytes']:,} bytes\n")
mdfile.write(f"**Contributors:** {summary['contributor_count']}\n\n")
# Category breakdown
mdfile.write("## File Categories\n\n")
for category, count in sorted(summary['categories'].items()):
mdfile.write(f"- **{category.replace('_', ' ').title()}:** {count} files\n")
mdfile.write("\n")
# Top contributors
mdfile.write("## Contributors\n\n")
for contrib in summary['top_contributors']:
mdfile.write(f"- **{contrib['name']}** ({contrib['email']}): ")
mdfile.write(f"{contrib['file_count']} files, {contrib['commit_count']} commits\n")
mdfile.write("\n")
# Detailed file listing by category
mdfile.write("## Detailed File Inventory\n\n")
for category in sorted(summary['categories'].keys()):
category_files = [f for f in self.file_inventory if f['category'] == category]
if not category_files:
continue
mdfile.write(f"### {category.replace('_', ' ').title()}\n\n")
for file_info in sorted(category_files, key=lambda x: x['path']):
mdfile.write(f"**{file_info['path']}**\n")
mdfile.write(f"- Primary Author: {file_info['primary_author']}\n")
mdfile.write(f"- Contributors: {file_info['contributor_count']}\n")
mdfile.write(f"- Size: {file_info['size_bytes']:,} bytes\n")
if file_info['contributors']:
contributors_str = ', '.join([c['name'] for c in file_info['contributors']])
mdfile.write(f"- All Contributors: {contributors_str}\n")
mdfile.write(f"- SHA-256: `{file_info['sha256_hash']}`\n\n")
print(f"Markdown export saved to: {output_path}")
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Generate legal file inventory for Basic Memory repository"
)
parser.add_argument(
'--repo-path', '-r',
default='.',
help='Path to repository (default: current directory)'
)
parser.add_argument(
'--output-dir', '-o',
default='./legal_inventory',
help='Output directory for reports (default: ./legal_inventory)'
)
parser.add_argument(
'--formats', '-f',
nargs='+',
choices=['csv', 'json', 'markdown', 'all'],
default=['all'],
help='Output formats to generate (default: all)'
)
args = parser.parse_args()
# Create output directory
output_dir = Path(args.output_dir)
output_dir.mkdir(exist_ok=True)
# Generate inventory
generator = LegalInventoryGenerator(args.repo_path)
generator.scan_repository()
# Determine formats to export
formats = args.formats
if 'all' in formats:
formats = ['csv', 'json', 'markdown']
# Export in requested formats
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
if 'csv' in formats:
generator.export_csv(output_dir / f'basic_memory_inventory_{timestamp}.csv')
if 'json' in formats:
generator.export_json(output_dir / f'basic_memory_inventory_{timestamp}.json')
if 'markdown' in formats:
generator.export_markdown(output_dir / f'basic_memory_inventory_{timestamp}.md')
print("\nLegal inventory generation complete!")
print(f"Output saved to: {output_dir}")
if __name__ == '__main__':
main()
+1 -1
View File
@@ -1,7 +1,7 @@
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
# Package version - updated by release automation
__version__ = "0.14.2"
__version__ = "0.14.4"
# API version for FastAPI - independent of package version
__api_version__ = "v0"
+3 -1
View File
@@ -8,17 +8,19 @@ from sqlalchemy import pool
from alembic import context
from basic_memory.config import ConfigManager
# set config.env to "test" for pytest to prevent logging to file in utils.setup_logging()
os.environ["BASIC_MEMORY_ENV"] = "test"
# Import after setting environment variable # noqa: E402
from basic_memory.config import app_config # noqa: E402
from basic_memory.models import Base # noqa: E402
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
app_config = ConfigManager().config
# Set the SQLAlchemy URL from our app config
sqlalchemy_url = f"sqlite:///{app_config.database_path}"
config.set_main_option("sqlalchemy.url", sqlalchemy_url)
@@ -0,0 +1,53 @@
"""fix project foreign keys
Revision ID: a1b2c3d4e5f6
Revises: 647e7a75e2cd
Create Date: 2025-08-19 22:06:00.000000
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a1b2c3d4e5f6"
down_revision: Union[str, None] = "647e7a75e2cd"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Re-establish foreign key constraints that were lost during project table recreation.
The migration 647e7a75e2cd recreated the project table but did not re-establish
the foreign key constraint from entity.project_id to project.id, causing
foreign key constraint failures when trying to delete projects with related entities.
"""
# SQLite doesn't allow adding foreign key constraints to existing tables easily
# We need to be careful and handle the case where the constraint might already exist
with op.batch_alter_table("entity", schema=None) as batch_op:
# Try to drop existing foreign key constraint (may not exist)
try:
batch_op.drop_constraint("fk_entity_project_id", type_="foreignkey")
except Exception:
# Constraint may not exist, which is fine - we'll create it next
pass
# Add the foreign key constraint with CASCADE DELETE
# This ensures that when a project is deleted, all related entities are also deleted
batch_op.create_foreign_key(
"fk_entity_project_id",
"project",
["project_id"],
["id"],
ondelete="CASCADE"
)
def downgrade() -> None:
"""Remove the foreign key constraint."""
with op.batch_alter_table("entity", schema=None) as batch_op:
batch_op.drop_constraint("fk_entity_project_id", type_="foreignkey")
+4 -1
View File
@@ -20,15 +20,18 @@ from basic_memory.api.routers import (
search,
prompt_router,
)
from basic_memory.config import app_config
from basic_memory.config import ConfigManager
from basic_memory.services.initialization import initialize_app, initialize_file_sync
@asynccontextmanager
async def lifespan(app: FastAPI): # pragma: no cover
"""Lifecycle manager for the FastAPI app."""
app_config = ConfigManager().config
# Initialize app and database
logger.info("Starting Basic Memory API")
print(f"fastapi {app_config.projects}")
await initialize_app(app_config)
logger.info(f"Sync changes enabled: {app_config.sync_changes}")
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Request
from loguru import logger
from pydantic import BaseModel
from basic_memory.config import app_config
from basic_memory.config import ConfigManager
from basic_memory.deps import SyncServiceDep, ProjectRepositoryDep
router = APIRouter(prefix="/management", tags=["management"])
@@ -41,6 +41,8 @@ async def start_watch_service(
# Watch service is already running
return WatchStatusResponse(running=True)
app_config = ConfigManager().config
# Create and start a new watch service
logger.info("Starting watch service via management API")
+21 -13
View File
@@ -1,5 +1,6 @@
"""Router for project management."""
import os
from fastapi import APIRouter, HTTPException, Path, Body
from typing import Optional
@@ -32,40 +33,47 @@ async def get_project_info(
@project_router.patch("/{name}", response_model=ProjectStatusResponse)
async def update_project(
project_service: ProjectServiceDep,
project_name: str = Path(..., description="Name of the project to update"),
path: Optional[str] = Body(None, description="New path for the project"),
name: str = Path(..., description="Name of the project to update"),
path: Optional[str] = Body(None, description="New absolute path for the project"),
is_active: Optional[bool] = Body(None, description="Status of the project (active/inactive)"),
) -> ProjectStatusResponse:
"""Update a project's information in configuration and database.
Args:
project_name: The name of the project to update
path: Optional new path for the project
name: The name of the project to update
path: Optional new absolute path for the project
is_active: Optional status update for the project
Returns:
Response confirming the project was updated
"""
try: # pragma: no cover
try:
# Validate that path is absolute if provided
if path and not os.path.isabs(path):
raise HTTPException(status_code=400, detail="Path must be absolute")
# Get original project info for the response
old_project_info = ProjectItem(
name=project_name,
path=project_service.projects.get(project_name, ""),
name=name,
path=project_service.projects.get(name, ""),
)
await project_service.update_project(project_name, updated_path=path, is_active=is_active)
if path:
await project_service.move_project(name, path)
elif is_active is not None:
await project_service.update_project(name, is_active=is_active)
# Get updated project info
updated_path = path if path else project_service.projects.get(project_name, "")
updated_path = path if path else project_service.projects.get(name, "")
return ProjectStatusResponse(
message=f"Project '{project_name}' updated successfully",
message=f"Project '{name}' updated successfully",
status="success",
default=(project_name == project_service.default_project),
default=(name == project_service.default_project),
old_project=old_project_info,
new_project=ProjectItem(name=project_name, path=updated_path),
new_project=ProjectItem(name=name, path=updated_path),
)
except ValueError as e: # pragma: no cover
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@@ -188,7 +188,7 @@ async def write_resource(
"content_type": content_type,
"file_path": file_path,
"checksum": checksum,
"updated_at": datetime.fromtimestamp(file_stats.st_mtime),
"updated_at": datetime.fromtimestamp(file_stats.st_mtime).astimezone(),
},
)
status_code = 200
@@ -200,8 +200,8 @@ async def write_resource(
content_type=content_type,
file_path=file_path,
checksum=checksum,
created_at=datetime.fromtimestamp(file_stats.st_ctime),
updated_at=datetime.fromtimestamp(file_stats.st_mtime),
created_at=datetime.fromtimestamp(file_stats.st_ctime).astimezone(),
updated_at=datetime.fromtimestamp(file_stats.st_mtime).astimezone(),
)
entity = await entity_repository.add(entity)
status_code = 201
+3 -3
View File
@@ -2,7 +2,7 @@ from typing import Optional
import typer
from basic_memory.config import get_project_config
from basic_memory.config import get_project_config, ConfigManager
from basic_memory.mcp.project_session import session
@@ -10,8 +10,8 @@ def version_callback(value: bool) -> None:
"""Show version and exit."""
if value: # pragma: no cover
import basic_memory
from basic_memory.config import config
config = get_project_config()
typer.echo(f"Basic Memory version: {basic_memory.__version__}")
typer.echo(f"Current project: {config.project}")
typer.echo(f"Project path: {config.home}")
@@ -44,9 +44,9 @@ def app_callback(
# Run initialization for every command unless --version was specified
if not version and ctx.invoked_subcommand is not None:
from basic_memory.config import app_config
from basic_memory.services.initialization import ensure_initialization
app_config = ConfigManager().config
ensure_initialization(app_config)
# Initialize MCP session with the specified project or default
+1 -2
View File
@@ -1,10 +1,9 @@
"""CLI commands for basic-memory."""
from . import auth, status, sync, db, import_memory_json, mcp, import_claude_conversations
from . import status, sync, db, import_memory_json, mcp, import_claude_conversations
from . import import_claude_projects, import_chatgpt, tool, project
__all__ = [
"auth",
"status",
"sync",
"db",
-136
View File
@@ -1,136 +0,0 @@
"""OAuth management commands."""
import typer
from typing import Optional
from pydantic import AnyHttpUrl
from basic_memory.cli.app import app
from basic_memory.mcp.auth_provider import BasicMemoryOAuthProvider
from mcp.shared.auth import OAuthClientInformationFull
auth_app = typer.Typer(help="OAuth client management commands")
app.add_typer(auth_app, name="auth")
@auth_app.command()
def register_client(
client_id: Optional[str] = typer.Option(
None, help="Client ID (auto-generated if not provided)"
),
client_secret: Optional[str] = typer.Option(
None, help="Client secret (auto-generated if not provided)"
),
issuer_url: str = typer.Option("http://localhost:8000", help="OAuth issuer URL"),
):
"""Register a new OAuth client for Basic Memory MCP server."""
# Create provider instance
provider = BasicMemoryOAuthProvider(issuer_url=issuer_url)
# Create client info with required redirect_uris
client_info = OAuthClientInformationFull(
client_id=client_id or "", # Provider will generate if empty
client_secret=client_secret or "", # Provider will generate if empty
redirect_uris=[AnyHttpUrl("http://localhost:8000/callback")], # Default redirect URI
client_name="Basic Memory OAuth Client",
grant_types=["authorization_code", "refresh_token"],
)
# Register the client
import asyncio
asyncio.run(provider.register_client(client_info))
typer.echo("Client registered successfully!")
typer.echo(f"Client ID: {client_info.client_id}")
typer.echo(f"Client Secret: {client_info.client_secret}")
typer.echo("\nSave these credentials securely - the client secret cannot be retrieved later.")
@auth_app.command()
def test_auth(
issuer_url: str = typer.Option("http://localhost:8000", help="OAuth issuer URL"),
):
"""Test OAuth authentication flow.
IMPORTANT: Use the same FASTMCP_AUTH_SECRET_KEY environment variable
as your MCP server for tokens to validate correctly.
"""
import asyncio
import secrets
from mcp.server.auth.provider import AuthorizationParams
from pydantic import AnyHttpUrl
async def test_flow():
# Create provider with same secret key as server
provider = BasicMemoryOAuthProvider(issuer_url=issuer_url)
# Register a test client
client_info = OAuthClientInformationFull(
client_id=secrets.token_urlsafe(16),
client_secret=secrets.token_urlsafe(32),
redirect_uris=[AnyHttpUrl("http://localhost:8000/callback")],
client_name="Test OAuth Client",
grant_types=["authorization_code", "refresh_token"],
)
await provider.register_client(client_info)
typer.echo(f"Registered test client: {client_info.client_id}")
# Get the client
client = await provider.get_client(client_info.client_id)
if not client:
typer.echo("Error: Client not found after registration", err=True)
return
# Create authorization request
auth_params = AuthorizationParams(
state="test-state",
scopes=["read", "write"],
code_challenge="test-challenge",
redirect_uri=AnyHttpUrl("http://localhost:8000/callback"),
redirect_uri_provided_explicitly=True,
)
# Get authorization URL
auth_url = await provider.authorize(client, auth_params)
typer.echo(f"Authorization URL: {auth_url}")
# Extract auth code from URL
from urllib.parse import urlparse, parse_qs
parsed = urlparse(auth_url)
params = parse_qs(parsed.query)
auth_code = params.get("code", [None])[0]
if not auth_code:
typer.echo("Error: No authorization code in URL", err=True)
return
# Load the authorization code
code_obj = await provider.load_authorization_code(client, auth_code)
if not code_obj:
typer.echo("Error: Invalid authorization code", err=True)
return
# Exchange for tokens
token = await provider.exchange_authorization_code(client, code_obj)
typer.echo(f"Access token: {token.access_token}")
typer.echo(f"Refresh token: {token.refresh_token}")
typer.echo(f"Expires in: {token.expires_in} seconds")
# Validate access token
access_token_obj = await provider.load_access_token(token.access_token)
if access_token_obj:
typer.echo("Access token validated successfully!")
typer.echo(f"Client ID: {access_token_obj.client_id}")
typer.echo(f"Scopes: {access_token_obj.scopes}")
else:
typer.echo("Error: Invalid access token", err=True)
asyncio.run(test_flow())
if __name__ == "__main__":
auth_app()
+5 -5
View File
@@ -1,14 +1,13 @@
"""Database management commands."""
import asyncio
from pathlib import Path
import typer
from loguru import logger
from basic_memory import db
from basic_memory.cli.app import app
from basic_memory.config import app_config, config_manager
from basic_memory.config import ConfigManager, BasicMemoryConfig, save_basic_memory_config
@app.command()
@@ -18,6 +17,8 @@ def reset(
"""Reset database (drop all tables and recreate)."""
if typer.confirm("This will delete all data in your db. Are you sure?"):
logger.info("Resetting database...")
config_manager = ConfigManager()
app_config = config_manager.config
# Get database path
db_path = app_config.app_database_path
@@ -27,9 +28,8 @@ def reset(
logger.info(f"Database file deleted: {db_path}")
# Reset project configuration
config_manager.config.projects = {"main": str(Path.home() / "basic-memory")}
config_manager.config.default_project = "main"
config_manager.save_config(config_manager.config)
config = BasicMemoryConfig()
save_basic_memory_config(config_manager.config_file, config)
logger.info("Project configuration reset to default")
# Create a new empty database
@@ -7,7 +7,7 @@ from typing import Annotated
import typer
from basic_memory.cli.app import import_app
from basic_memory.config import config
from basic_memory.config import get_project_config
from basic_memory.importers import ChatGPTImporter
from basic_memory.markdown import EntityParser, MarkdownProcessor
from loguru import logger
@@ -19,6 +19,7 @@ console = Console()
async def get_markdown_processor() -> MarkdownProcessor:
"""Get MarkdownProcessor instance."""
config = get_project_config()
entity_parser = EntityParser(config.home)
return MarkdownProcessor(entity_parser)
@@ -49,7 +50,7 @@ def import_chatgpt(
# Get markdown processor
markdown_processor = asyncio.run(get_markdown_processor())
config = get_project_config()
# Process the file
base_path = config.home / folder
console.print(f"\nImporting chats from {conversations_json}...writing to {base_path}")
@@ -7,7 +7,7 @@ from typing import Annotated
import typer
from basic_memory.cli.app import claude_app
from basic_memory.config import config
from basic_memory.config import get_project_config
from basic_memory.importers.claude_conversations_importer import ClaudeConversationsImporter
from basic_memory.markdown import EntityParser, MarkdownProcessor
from loguru import logger
@@ -19,6 +19,7 @@ console = Console()
async def get_markdown_processor() -> MarkdownProcessor:
"""Get MarkdownProcessor instance."""
config = get_project_config()
entity_parser = EntityParser(config.home)
return MarkdownProcessor(entity_parser)
@@ -42,6 +43,7 @@ def import_claude(
After importing, run 'basic-memory sync' to index the new files.
"""
config = get_project_config()
try:
if not conversations_json.exists():
typer.echo(f"Error: File not found: {conversations_json}", err=True)
@@ -7,7 +7,7 @@ from typing import Annotated
import typer
from basic_memory.cli.app import claude_app
from basic_memory.config import config
from basic_memory.config import get_project_config
from basic_memory.importers.claude_projects_importer import ClaudeProjectsImporter
from basic_memory.markdown import EntityParser, MarkdownProcessor
from loguru import logger
@@ -19,6 +19,7 @@ console = Console()
async def get_markdown_processor() -> MarkdownProcessor:
"""Get MarkdownProcessor instance."""
config = get_project_config()
entity_parser = EntityParser(config.home)
return MarkdownProcessor(entity_parser)
@@ -41,6 +42,7 @@ def import_projects(
After importing, run 'basic-memory sync' to index the new files.
"""
config = get_project_config()
try:
if not projects_json.exists():
typer.echo(f"Error: File not found: {projects_json}", err=True)
@@ -7,7 +7,7 @@ from typing import Annotated
import typer
from basic_memory.cli.app import import_app
from basic_memory.config import config
from basic_memory.config import get_project_config
from basic_memory.importers.memory_json_importer import MemoryJsonImporter
from basic_memory.markdown import EntityParser, MarkdownProcessor
from loguru import logger
@@ -19,6 +19,7 @@ console = Console()
async def get_markdown_processor() -> MarkdownProcessor:
"""Get MarkdownProcessor instance."""
config = get_project_config()
entity_parser = EntityParser(config.home)
return MarkdownProcessor(entity_parser)
@@ -46,6 +47,7 @@ def memory_json(
typer.echo(f"Error: File not found: {json_path}", err=True)
raise typer.Exit(1)
config = get_project_config()
try:
# Get markdown processor
markdown_processor = asyncio.run(get_markdown_processor())
@@ -74,7 +76,8 @@ def memory_json(
Panel(
f"[green]Import complete![/green]\n\n"
f"Created {result.entities} entities\n"
f"Added {result.relations} relations",
f"Added {result.relations} relations\n"
f"Skipped {result.skipped_entities} entities\n",
expand=False,
)
)
+3 -15
View File
@@ -4,6 +4,7 @@ import asyncio
import typer
from basic_memory.cli.app import app
from basic_memory.config import ConfigManager
# Import mcp instance
from basic_memory.mcp.server import mcp as mcp_server # pragma: no cover
@@ -34,26 +35,13 @@ def mcp(
- sse: Server-Sent Events (for compatibility with existing clients)
"""
# Check if OAuth is enabled
import os
auth_enabled = os.getenv("FASTMCP_AUTH_ENABLED", "false").lower() == "true"
if auth_enabled:
logger.info("OAuth authentication is ENABLED")
logger.info(f"Issuer URL: {os.getenv('FASTMCP_AUTH_ISSUER_URL', 'http://localhost:8000')}")
if os.getenv("FASTMCP_AUTH_REQUIRED_SCOPES"):
logger.info(f"Required scopes: {os.getenv('FASTMCP_AUTH_REQUIRED_SCOPES')}")
else:
logger.info("OAuth authentication is DISABLED")
from basic_memory.config import app_config
from basic_memory.services.initialization import initialize_file_sync
# Start the MCP server with the specified transport
# Use unified thread-based sync approach for both transports
import threading
app_config = ConfigManager().config
def run_file_sync():
"""Run file sync in a separate thread with its own event loop."""
loop = asyncio.new_event_loop()
+46 -6
View File
@@ -24,6 +24,7 @@ from basic_memory.schemas.project_info import ProjectStatusResponse
from basic_memory.mcp.tools.utils import call_delete
from basic_memory.mcp.tools.utils import call_put
from basic_memory.utils import generate_permalink
from basic_memory.mcp.tools.utils import call_patch
console = Console()
@@ -73,7 +74,7 @@ def add_project(
) -> None:
"""Add a new project."""
# Resolve to absolute path
resolved_path = os.path.abspath(os.path.expanduser(path))
resolved_path = Path(os.path.abspath(os.path.expanduser(path))).as_posix()
try:
data = {"name": name, "path": resolved_path, "set_default": set_default}
@@ -99,8 +100,8 @@ def remove_project(
) -> None:
"""Remove a project from configuration."""
try:
project_name = generate_permalink(name)
response = asyncio.run(call_delete(client, f"/projects/{project_name}"))
project_permalink = generate_permalink(name)
response = asyncio.run(call_delete(client, f"/projects/{project_permalink}"))
result = ProjectStatusResponse.model_validate(response.json())
console.print(f"[green]{result.message}[/green]")
@@ -118,9 +119,8 @@ def set_default_project(
) -> None:
"""Set the default project and activate it for the current session."""
try:
project_name = generate_permalink(name)
response = asyncio.run(call_put(client, f"/projects/{project_name}/default"))
project_permalink = generate_permalink(name)
response = asyncio.run(call_put(client, f"/projects/{project_permalink}/default"))
result = ProjectStatusResponse.model_validate(response.json())
console.print(f"[green]{result.message}[/green]")
@@ -148,6 +148,46 @@ def synchronize_projects() -> None:
raise typer.Exit(1)
@project_app.command("move")
def move_project(
name: str = typer.Argument(..., help="Name of the project to move"),
new_path: str = typer.Argument(..., help="New absolute path for the project"),
) -> None:
"""Move a project to a new location."""
# Resolve to absolute path
resolved_path = Path(os.path.abspath(os.path.expanduser(new_path))).as_posix()
try:
data = {"path": resolved_path}
project_permalink = generate_permalink(name)
current_project = session.get_current_project()
response = asyncio.run(
call_patch(client, f"/{current_project}/project/{project_permalink}", json=data)
)
result = ProjectStatusResponse.model_validate(response.json())
console.print(f"[green]{result.message}[/green]")
# Show important file movement reminder
console.print() # Empty line for spacing
console.print(
Panel(
"[bold red]IMPORTANT:[/bold red] Project configuration updated successfully.\n\n"
"[yellow]You must manually move your project files from the old location to:[/yellow]\n"
f"[cyan]{resolved_path}[/cyan]\n\n"
"[dim]Basic Memory has only updated the configuration - your files remain in their original location.[/dim]",
title="⚠️ Manual File Movement Required",
border_style="yellow",
expand=False,
)
)
except Exception as e:
console.print(f"[red]Error moving project: {str(e)}[/red]")
raise typer.Exit(1)
@project_app.command("info")
def display_project_info(
json_output: bool = typer.Option(False, "--json", help="Output in JSON format"),
+4 -1
View File
@@ -12,7 +12,7 @@ from rich.tree import Tree
from basic_memory import db
from basic_memory.cli.app import app
from basic_memory.cli.commands.sync import get_sync_service
from basic_memory.config import config, app_config
from basic_memory.config import ConfigManager, get_project_config
from basic_memory.repository import ProjectRepository
from basic_memory.sync.sync_service import SyncReport
@@ -126,6 +126,9 @@ async def run_status(verbose: bool = False): # pragma: no cover
"""Check sync status of files vs database."""
# Check knowledge/ directory
app_config = ConfigManager().config
config = get_project_config()
_, session_maker = await db.get_or_create_db(
db_path=app_config.database_path, db_type=db.DatabaseType.FILESYSTEM
)
+10 -2
View File
@@ -13,7 +13,7 @@ from rich.tree import Tree
from basic_memory import db
from basic_memory.cli.app import app
from basic_memory.config import config
from basic_memory.config import ConfigManager, get_project_config
from basic_memory.markdown import EntityParser
from basic_memory.markdown.markdown_processor import MarkdownProcessor
from basic_memory.models import Project
@@ -29,7 +29,6 @@ from basic_memory.services.link_resolver import LinkResolver
from basic_memory.services.search_service import SearchService
from basic_memory.sync import SyncService
from basic_memory.sync.sync_service import SyncReport
from basic_memory.config import app_config
console = Console()
@@ -42,6 +41,8 @@ class ValidationIssue:
async def get_sync_service(project: Project) -> SyncService: # pragma: no cover
"""Get sync service instance with all dependencies."""
app_config = ConfigManager().config
_, session_maker = await db.get_or_create_db(
db_path=app_config.database_path, db_type=db.DatabaseType.FILESYSTEM
)
@@ -96,6 +97,7 @@ def group_issues_by_directory(issues: List[ValidationIssue]) -> Dict[str, List[V
def display_sync_summary(knowledge: SyncReport):
"""Display a one-line summary of sync changes."""
config = get_project_config()
total_changes = knowledge.total
project_name = config.project
@@ -124,6 +126,7 @@ def display_sync_summary(knowledge: SyncReport):
def display_detailed_sync_results(knowledge: SyncReport):
"""Display detailed sync results with trees."""
config = get_project_config()
project_name = config.project
if knowledge.total == 0:
@@ -158,6 +161,9 @@ def display_detailed_sync_results(knowledge: SyncReport):
async def run_sync(verbose: bool = False):
"""Run sync operation."""
app_config = ConfigManager().config
config = get_project_config()
_, session_maker = await db.get_or_create_db(
db_path=app_config.database_path, db_type=db.DatabaseType.FILESYSTEM
)
@@ -212,6 +218,8 @@ def sync(
),
) -> None:
"""Sync knowledge files with the database."""
config = get_project_config()
try:
# Show which project we're syncing
typer.echo(f"Syncing project: {config.project}")
-1
View File
@@ -4,7 +4,6 @@ from basic_memory.cli.app import app # pragma: no cover
# Register commands
from basic_memory.cli.commands import ( # noqa: F401 # pragma: no cover
auth,
db,
import_chatgpt,
import_claude_conversations,
+61 -34
View File
@@ -46,7 +46,7 @@ class BasicMemoryConfig(BaseSettings):
projects: Dict[str, str] = Field(
default_factory=lambda: {
"main": str(Path(os.getenv("BASIC_MEMORY_HOME", Path.home() / "basic-memory")))
"main": Path(os.getenv("BASIC_MEMORY_HOME", Path.home() / "basic-memory")).as_posix()
},
description="Mapping of project names to their filesystem paths",
)
@@ -74,6 +74,17 @@ class BasicMemoryConfig(BaseSettings):
description="Whether to sync changes in real time. default (True)",
)
kebab_filenames: bool = Field(
default=False,
description="Format for generated filenames. False preserves spaces and special chars, True converts them to hyphens for consistency with permalinks",
)
# API connection configuration
api_url: Optional[str] = Field(
default=None,
description="URL of remote Basic Memory API. If set, MCP will connect to this API instead of using local ASGI transport.",
)
model_config = SettingsConfigDict(
env_prefix="BASIC_MEMORY_",
extra="ignore",
@@ -94,9 +105,9 @@ class BasicMemoryConfig(BaseSettings):
"""Ensure configuration is valid after initialization."""
# Ensure main project exists
if "main" not in self.projects: # pragma: no cover
self.projects["main"] = str(
self.projects["main"] = (
Path(os.getenv("BASIC_MEMORY_HOME", Path.home() / "basic-memory"))
)
).as_posix()
# Ensure default project is valid
if self.default_project not in self.projects: # pragma: no cover
@@ -124,6 +135,7 @@ class BasicMemoryConfig(BaseSettings):
"""
# Load the app-level database path from the global config
config_manager = ConfigManager()
config = config_manager.load_config() # pragma: no cover
return config.app_database_path # pragma: no cover
@@ -162,20 +174,21 @@ class ConfigManager:
# Ensure config directory exists
self.config_dir.mkdir(parents=True, exist_ok=True)
# Load or create configuration
self.config = self.load_config()
@property
def config(self) -> BasicMemoryConfig:
"""Get configuration, loading it lazily if needed."""
return self.load_config()
def load_config(self) -> BasicMemoryConfig:
"""Load configuration from file or create default."""
if self.config_file.exists():
try:
data = json.loads(self.config_file.read_text(encoding="utf-8"))
return BasicMemoryConfig(**data)
except Exception as e: # pragma: no cover
logger.error(f"Failed to load config: {e}")
config = BasicMemoryConfig()
self.save_config(config)
return config
logger.exception(f"Failed to load config: {e}")
raise e
else:
config = BasicMemoryConfig()
self.save_config(config)
@@ -183,10 +196,7 @@ class ConfigManager:
def save_config(self, config: BasicMemoryConfig) -> None:
"""Save configuration to file."""
try:
self.config_file.write_text(json.dumps(config.model_dump(), indent=2))
except Exception as e: # pragma: no cover
logger.error(f"Failed to save config: {e}")
save_basic_memory_config(self.config_file, config)
@property
def projects(self) -> Dict[str, str]:
@@ -208,8 +218,10 @@ class ConfigManager:
project_path = Path(path)
project_path.mkdir(parents=True, exist_ok=True) # pragma: no cover
self.config.projects[name] = str(project_path)
self.save_config(self.config)
# Load config, modify it, and save it
config = self.load_config()
config.projects[name] = project_path.as_posix()
self.save_config(config)
return ProjectConfig(name=name, home=project_path)
def remove_project(self, name: str) -> None:
@@ -219,11 +231,13 @@ class ConfigManager:
if not project_name: # pragma: no cover
raise ValueError(f"Project '{name}' not found")
if project_name == self.config.default_project: # pragma: no cover
# Load config, check, modify, and save
config = self.load_config()
if project_name == config.default_project: # pragma: no cover
raise ValueError(f"Cannot remove the default project '{name}'")
del self.config.projects[name]
self.save_config(self.config)
del config.projects[name]
self.save_config(config)
def set_default_project(self, name: str) -> None:
"""Set the default project."""
@@ -231,15 +245,18 @@ class ConfigManager:
if not project_name: # pragma: no cover
raise ValueError(f"Project '{name}' not found")
self.config.default_project = name
self.save_config(self.config)
# Load config, modify, and save
config = self.load_config()
config.default_project = project_name
self.save_config(config)
def get_project(self, name: str) -> Tuple[str, str] | Tuple[None, None]:
"""Look up a project from the configuration by name or permalink"""
project_permalink = generate_permalink(name)
for name, path in app_config.projects.items():
if project_permalink == generate_permalink(name):
return name, path
app_config = self.config
for project_name, path in app_config.projects.items():
if project_permalink == generate_permalink(project_name):
return project_name, path
return None, None
@@ -252,7 +269,7 @@ def get_project_config(project_name: Optional[str] = None) -> ProjectConfig:
actual_project_name = None
# load the config from file
global app_config
config_manager = ConfigManager()
app_config = config_manager.load_config()
# Get project name from environment variable
@@ -282,14 +299,12 @@ def get_project_config(project_name: Optional[str] = None) -> ProjectConfig:
raise ValueError(f"Project '{actual_project_name}' not found") # pragma: no cover
# Create config manager
config_manager = ConfigManager()
# Export the app-level config
app_config: BasicMemoryConfig = config_manager.config
# Load project config for the default project (backward compatibility)
config: ProjectConfig = get_project_config()
def save_basic_memory_config(file_path: Path, config: BasicMemoryConfig) -> None:
"""Save configuration to file."""
try:
file_path.write_text(json.dumps(config.model_dump(), indent=2))
except Exception as e: # pragma: no cover
logger.error(f"Failed to save config: {e}")
def update_current_project(project_name: str) -> None:
@@ -341,12 +356,24 @@ def setup_basic_memory_logging(): # pragma: no cover
# print("Skipping duplicate logging setup")
return
# Check for console logging environment variable - accept more truthy values
console_logging_env = os.getenv("BASIC_MEMORY_CONSOLE_LOGGING", "false").lower()
console_logging = console_logging_env in ("true", "1", "yes", "on")
# Check for log level environment variable first, fall back to config
log_level = os.getenv("BASIC_MEMORY_LOG_LEVEL")
if not log_level:
config_manager = ConfigManager()
log_level = config_manager.config.log_level
config_manager = ConfigManager()
config = get_project_config()
setup_logging(
env=config_manager.config.env,
home_dir=user_home, # Use user home for logs
log_level=config_manager.load_config().log_level,
log_level=log_level,
log_file=f"{DATA_DIR_NAME}/basic-memory-{process_name}.log",
console=False,
console=console_logging,
)
logger.info(f"Basic Memory {basic_memory.__version__} (Project: {config.project})")
+2 -6
View File
@@ -4,7 +4,7 @@ from enum import Enum, auto
from pathlib import Path
from typing import AsyncGenerator, Optional
from basic_memory.config import BasicMemoryConfig
from basic_memory.config import BasicMemoryConfig, ConfigManager
from alembic import command
from alembic.config import Config
@@ -88,7 +88,6 @@ async def get_or_create_db(
db_path: Path,
db_type: DatabaseType = DatabaseType.FILESYSTEM,
ensure_migrations: bool = True,
app_config: Optional["BasicMemoryConfig"] = None,
) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]: # pragma: no cover
"""Get or create database engine and session maker."""
global _engine, _session_maker
@@ -98,10 +97,7 @@ async def get_or_create_db(
# Run migrations automatically unless explicitly disabled
if ensure_migrations:
if app_config is None:
from basic_memory.config import app_config as global_app_config
app_config = global_app_config
app_config = ConfigManager().config
await run_migrations(app_config, db_type)
# These checks should never fail since we just created the engine and session maker
+3 -2
View File
@@ -12,7 +12,7 @@ from sqlalchemy.ext.asyncio import (
import pathlib
from basic_memory import db
from basic_memory.config import ProjectConfig, BasicMemoryConfig
from basic_memory.config import ProjectConfig, BasicMemoryConfig, ConfigManager
from basic_memory.importers import (
ChatGPTImporter,
ClaudeConversationsImporter,
@@ -33,10 +33,10 @@ from basic_memory.services.file_service import FileService
from basic_memory.services.link_resolver import LinkResolver
from basic_memory.services.search_service import SearchService
from basic_memory.sync import SyncService
from basic_memory.config import app_config
def get_app_config() -> BasicMemoryConfig: # pragma: no cover
app_config = ConfigManager().config
return app_config
@@ -297,6 +297,7 @@ ContextServiceDep = Annotated[ContextService, Depends(get_context_service)]
async def get_sync_service(
app_config: AppConfigDep,
entity_service: EntityServiceDep,
entity_parser: EntityParserDep,
entity_repository: EntityRepositoryDep,
+65
View File
@@ -2,9 +2,11 @@
import hashlib
from pathlib import Path
import re
from typing import Any, Dict, Union
import yaml
import frontmatter
from loguru import logger
from basic_memory.utils import FilePath
@@ -233,3 +235,66 @@ async def update_frontmatter(path: FilePath, updates: Dict[str, Any]) -> str:
error=str(e),
)
raise FileError(f"Failed to update frontmatter: {e}")
def dump_frontmatter(post: frontmatter.Post) -> str:
"""
Serialize frontmatter.Post to markdown with Obsidian-compatible YAML format.
This function ensures that tags are formatted as YAML lists instead of JSON arrays:
Good (Obsidian compatible):
---
tags:
- system
- overview
- reference
---
Bad (current behavior):
---
tags: ["system", "overview", "reference"]
---
Args:
post: frontmatter.Post object to serialize
Returns:
String containing markdown with properly formatted YAML frontmatter
"""
if not post.metadata:
# No frontmatter, just return content
return post.content
# Serialize YAML with block style for lists
yaml_str = yaml.dump(
post.metadata,
sort_keys=False,
allow_unicode=True,
default_flow_style=False
)
# Construct the final markdown with frontmatter
if post.content:
return f"---\n{yaml_str}---\n\n{post.content}"
else:
return f"---\n{yaml_str}---\n"
def sanitize_for_filename(text: str, replacement: str = "-") -> str:
"""
Sanitize string to be safe for use as a note title
Replaces path separators and other problematic characters
with hyphens.
"""
# replace both POSIX and Windows path separators
text = re.sub(r"[/\\]", replacement, text)
# replace some other problematic chars
text = re.sub(r'[<>:"|?*]', replacement, text)
# compress multiple, repeated replacements
text = re.sub(f"{re.escape(replacement)}+", replacement, text)
return text.strip(replacement)
+20 -10
View File
@@ -93,7 +93,7 @@ class ChatGPTImporter(Importer[ChatImportResult]):
break
# Generate permalink
date_prefix = datetime.fromtimestamp(created_at).strftime("%Y%m%d")
date_prefix = datetime.fromtimestamp(created_at).astimezone().strftime("%Y%m%d")
clean_title = clean_filename(conversation["title"])
# Format content
@@ -193,7 +193,7 @@ class ChatGPTImporter(Importer[ChatImportResult]):
def _traverse_messages(
self, mapping: Dict[str, Any], root_id: Optional[str], seen: Set[str]
) -> List[Dict[str, Any]]: # pragma: no cover
"""Traverse message tree and return messages in order.
"""Traverse message tree iteratively to handle deep conversations.
Args:
mapping: Message mapping.
@@ -204,19 +204,29 @@ class ChatGPTImporter(Importer[ChatImportResult]):
List of message data.
"""
messages = []
node = mapping.get(root_id) if root_id else None
if not root_id:
return messages
while node:
# Use iterative approach with stack to avoid recursion depth issues
stack = [root_id]
while stack:
node_id = stack.pop()
if not node_id:
continue
node = mapping.get(node_id)
if not node:
continue
# Process current node if it has a message and hasn't been seen
if node["id"] not in seen and node.get("message"):
seen.add(node["id"])
messages.append(node["message"])
# Follow children
# Add children to stack in reverse order to maintain conversation flow
children = node.get("children", [])
for child_id in children:
child_msgs = self._traverse_messages(mapping, child_id, seen)
messages.extend(child_msgs)
break # Don't follow siblings
for child_id in reversed(children):
stack.append(child_id)
return messages
@@ -3,7 +3,7 @@
import logging
from typing import Any, Dict, List
from basic_memory.config import config
from basic_memory.config import get_project_config
from basic_memory.markdown.schemas import EntityFrontmatter, EntityMarkdown, Observation, Relation
from basic_memory.importers.base import Importer
from basic_memory.schemas.importer import EntityImportResult
@@ -27,10 +27,12 @@ class MemoryJsonImporter(Importer[EntityImportResult]):
Returns:
EntityImportResult containing statistics and status of the import.
"""
config = get_project_config()
try:
# First pass - collect all relations by source entity
entity_relations: Dict[str, List[Relation]] = {}
entities: Dict[str, Dict[str, Any]] = {}
skipped_entities: int = 0
# Ensure the base path exists
base_path = config.home # pragma: no cover
@@ -41,7 +43,13 @@ class MemoryJsonImporter(Importer[EntityImportResult]):
for line in source_data:
data = line
if data["type"] == "entity":
entities[data["name"]] = data
# Handle different possible name keys
entity_name = data.get("name") or data.get("entityName") or data.get("id")
if not entity_name:
logger.warning(f"Entity missing name field: {data}")
skipped_entities += 1
continue
entities[entity_name] = data
elif data["type"] == "relation":
# Store relation with its source entity
source = data.get("from") or data.get("from_id")
@@ -57,25 +65,31 @@ class MemoryJsonImporter(Importer[EntityImportResult]):
# Second pass - create and write entities
entities_created = 0
for name, entity_data in entities.items():
# Get entity type with fallback
entity_type = entity_data.get("entityType") or entity_data.get("type") or "entity"
# Ensure entity type directory exists
entity_type_dir = base_path / entity_data["entityType"]
entity_type_dir = base_path / entity_type
entity_type_dir.mkdir(parents=True, exist_ok=True)
# Get observations with fallback to empty list
observations = entity_data.get("observations", [])
entity = EntityMarkdown(
frontmatter=EntityFrontmatter(
metadata={
"type": entity_data["entityType"],
"type": entity_type,
"title": name,
"permalink": f"{entity_data['entityType']}/{name}",
"permalink": f"{entity_type}/{name}",
}
),
content=f"# {name}\n",
observations=[Observation(content=obs) for obs in entity_data["observations"]],
observations=[Observation(content=obs) for obs in observations],
relations=entity_relations.get(name, []),
)
# Write entity file
file_path = base_path / f"{entity_data['entityType']}/{name}.md"
file_path = base_path / f"{entity_type}/{name}.md"
await self.write_entity(entity, file_path)
entities_created += 1
@@ -86,6 +100,7 @@ class MemoryJsonImporter(Importer[EntityImportResult]):
success=True,
entities=entities_created,
relations=relations_count,
skipped_entities=skipped_entities,
)
except Exception as e: # pragma: no cover
+2 -2
View File
@@ -43,13 +43,13 @@ def format_timestamp(timestamp: Any) -> str: # pragma: no cover
except ValueError:
try:
# Try unix timestamp as string
timestamp = datetime.fromtimestamp(float(timestamp))
timestamp = datetime.fromtimestamp(float(timestamp)).astimezone()
except ValueError:
# Return as is if we can't parse it
return timestamp
elif isinstance(timestamp, (int, float)):
# Unix timestamp
timestamp = datetime.fromtimestamp(timestamp)
timestamp = datetime.fromtimestamp(timestamp).astimezone()
if isinstance(timestamp, datetime):
return timestamp.strftime("%Y-%m-%d %H:%M:%S")
+2 -2
View File
@@ -130,6 +130,6 @@ class EntityParser:
content=post.content,
observations=entity_content.observations,
relations=entity_content.relations,
created=datetime.fromtimestamp(file_stats.st_ctime),
modified=datetime.fromtimestamp(file_stats.st_mtime),
created=datetime.fromtimestamp(file_stats.st_ctime).astimezone(),
modified=datetime.fromtimestamp(file_stats.st_mtime).astimezone(),
)
@@ -2,11 +2,11 @@ from pathlib import Path
from typing import Optional
from collections import OrderedDict
import frontmatter
from frontmatter import Post
from loguru import logger
from basic_memory import file_utils
from basic_memory.file_utils import dump_frontmatter
from basic_memory.markdown.entity_parser import EntityParser
from basic_memory.markdown.schemas import EntityMarkdown, Observation, Relation
@@ -115,7 +115,7 @@ class MarkdownProcessor:
# Create Post object for frontmatter
post = Post(content, **frontmatter_dict)
final_content = frontmatter.dumps(post, sort_keys=False)
final_content = dump_frontmatter(post)
logger.debug(f"writing file {path} with content:\n{final_content}")
+42 -26
View File
@@ -8,35 +8,49 @@ from markdown_it.token import Token
# Observation handling functions
def is_observation(token: Token) -> bool:
"""Check if token looks like our observation format."""
import re
if token.type != "inline": # pragma: no cover
return False
content = token.content.strip()
# Use token.tag which contains the actual content for test tokens, fallback to content
content = (token.tag or token.content).strip()
if not content: # pragma: no cover
return False
# if it's a markdown_task, return false
if content.startswith("[ ]") or content.startswith("[x]") or content.startswith("[-]"):
return False
has_category = content.startswith("[") and "]" in content
# Exclude markdown links: [text](url)
if re.match(r"^\[.*?\]\(.*?\)$", content):
return False
# Exclude wiki links: [[text]]
if re.match(r"^\[\[.*?\]\]$", content):
return False
# Check for proper observation format: [category] content
match = re.match(r"^\[([^\[\]()]+)\]\s+(.+)", content)
has_tags = "#" in content
return has_category or has_tags
return bool(match) or has_tags
def parse_observation(token: Token) -> Dict[str, Any]:
"""Extract observation parts from token."""
# Strip bullet point if present
content = token.content.strip()
# Parse [category]
import re
# Use token.tag which contains the actual content for test tokens, fallback to content
content = (token.tag or token.content).strip()
# Parse [category] with regex
match = re.match(r"^\[([^\[\]()]+)\]\s+(.+)", content)
category = None
if content.startswith("["):
end = content.find("]")
if end != -1:
category = content[1:end].strip() or None # Convert empty to None
content = content[end + 1 :].strip()
if match:
category = match.group(1).strip()
content = match.group(2).strip()
else:
# Handle empty brackets [] followed by content
empty_match = re.match(r"^\[\]\s+(.+)", content)
if empty_match:
content = empty_match.group(1).strip()
# Parse (context)
context = None
if content.endswith(")"):
@@ -44,20 +58,18 @@ def parse_observation(token: Token) -> Dict[str, Any]:
if start != -1:
context = content[start + 1 : -1].strip()
content = content[:start].strip()
# Extract tags and keep original content
tags = []
parts = content.split()
for part in parts:
if part.startswith("#"):
# Handle multiple #tags stuck together
if "#" in part[1:]:
# Split on # but keep non-empty tags
subtags = [t for t in part.split("#") if t]
tags.extend(subtags)
else:
tags.append(part[1:])
return {
"category": category,
"content": content,
@@ -72,14 +84,16 @@ def is_explicit_relation(token: Token) -> bool:
if token.type != "inline": # pragma: no cover
return False
content = token.content.strip()
# Use token.tag which contains the actual content for test tokens, fallback to content
content = (token.tag or token.content).strip()
return "[[" in content and "]]" in content
def parse_relation(token: Token) -> Dict[str, Any] | None:
"""Extract relation parts from token."""
# Remove bullet point if present
content = token.content.strip()
# Use token.tag which contains the actual content for test tokens, fallback to content
content = (token.tag or token.content).strip()
# Extract [[target]]
target = None
@@ -213,10 +227,12 @@ def relation_plugin(md: MarkdownIt) -> None:
token.meta["relations"] = [rel]
# Always check for inline links in any text
elif "[[" in token.content:
rels = parse_inline_relations(token.content)
if rels:
token.meta["relations"] = token.meta.get("relations", []) + rels
else:
content = token.tag or token.content
if "[[" in content:
rels = parse_inline_relations(content)
if rels:
token.meta["relations"] = token.meta.get("relations", []) + rels
# Add the rule after inline processing
md.core.ruler.after("inline", "relations", relation_rule)
+1 -1
View File
@@ -41,7 +41,7 @@ def entity_model_from_markdown(
# Only update permalink if it exists in frontmatter, otherwise preserve existing
if markdown.frontmatter.permalink is not None:
model.permalink = markdown.frontmatter.permalink
model.file_path = str(file_path)
model.file_path = file_path.as_posix()
model.content_type = "text/markdown"
model.created_at = markdown.created
model.updated_at = markdown.modified
+22 -2
View File
@@ -1,8 +1,28 @@
from httpx import ASGITransport, AsyncClient
from loguru import logger
from basic_memory.api.app import app as fastapi_app
from basic_memory.config import ConfigManager
def create_client() -> AsyncClient:
"""Create an HTTP client based on configuration.
Returns:
AsyncClient configured for either local ASGI or remote HTTP transport
"""
config_manager = ConfigManager()
config = config_manager.load_config()
if config.api_url:
# Use HTTP transport for remote API
logger.info(f"Creating HTTP client for remote Basic Memory API: {config.api_url}")
return AsyncClient(base_url=config.api_url)
else:
# Use ASGI transport for local API
logger.debug("Creating ASGI client for local Basic Memory API")
return AsyncClient(transport=ASGITransport(app=fastapi_app), base_url="http://test")
BASE_URL = "http://test"
# Create shared async client
client = AsyncClient(transport=ASGITransport(app=fastapi_app), base_url=BASE_URL)
client = create_client()
-270
View File
@@ -1,270 +0,0 @@
"""OAuth authentication provider for Basic Memory MCP server."""
import secrets
from datetime import datetime, timedelta, timezone
from typing import Dict, Optional
import jwt
from mcp.server.auth.provider import (
OAuthAuthorizationServerProvider,
AuthorizationParams,
AuthorizationCode,
RefreshToken,
AccessToken,
)
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
from loguru import logger
class BasicMemoryAuthorizationCode(AuthorizationCode):
"""Extended authorization code with additional metadata."""
issuer_state: Optional[str] = None
class BasicMemoryRefreshToken(RefreshToken):
"""Extended refresh token with additional metadata."""
pass
class BasicMemoryAccessToken(AccessToken):
"""Extended access token with additional metadata."""
pass
class BasicMemoryOAuthProvider(
OAuthAuthorizationServerProvider[
BasicMemoryAuthorizationCode, BasicMemoryRefreshToken, BasicMemoryAccessToken
]
):
"""OAuth provider for Basic Memory MCP server.
This is a simple in-memory implementation that can be extended
to integrate with external OAuth providers or use persistent storage.
"""
def __init__(self, issuer_url: str = "http://localhost:8000", secret_key: Optional[str] = None):
self.issuer_url = issuer_url
# Use environment variable for secret key if available, otherwise generate
import os
self.secret_key = (
secret_key or os.getenv("FASTMCP_AUTH_SECRET_KEY") or secrets.token_urlsafe(32)
)
# In-memory storage - in production, use a proper database
self.clients: Dict[str, OAuthClientInformationFull] = {}
self.authorization_codes: Dict[str, BasicMemoryAuthorizationCode] = {}
self.refresh_tokens: Dict[str, BasicMemoryRefreshToken] = {}
self.access_tokens: Dict[str, BasicMemoryAccessToken] = {}
async def get_client(self, client_id: str) -> Optional[OAuthClientInformationFull]:
"""Get a client by ID."""
return self.clients.get(client_id)
async def register_client(self, client_info: OAuthClientInformationFull) -> None:
"""Register a new OAuth client."""
# Generate client ID if not provided
if not client_info.client_id:
client_info.client_id = secrets.token_urlsafe(16)
# Generate client secret if not provided
if not client_info.client_secret:
client_info.client_secret = secrets.token_urlsafe(32)
self.clients[client_info.client_id] = client_info
logger.info(f"Registered OAuth client: {client_info.client_id}")
async def authorize(
self, client: OAuthClientInformationFull, params: AuthorizationParams
) -> str:
"""Create an authorization URL for the OAuth flow.
For basic-memory, we'll implement a simple authorization flow.
In production, this might redirect to an external provider.
"""
# Generate authorization code
auth_code = secrets.token_urlsafe(32)
# Store authorization code with metadata
self.authorization_codes[auth_code] = BasicMemoryAuthorizationCode(
code=auth_code,
scopes=params.scopes or [],
expires_at=(datetime.now(timezone.utc) + timedelta(minutes=10)).timestamp(),
client_id=client.client_id,
code_challenge=params.code_challenge,
redirect_uri=params.redirect_uri,
redirect_uri_provided_explicitly=params.redirect_uri_provided_explicitly,
issuer_state=params.state,
)
# In a real implementation, we'd redirect to an authorization page
# For now, we'll just return the redirect URL with the code
redirect_uri = str(params.redirect_uri)
separator = "&" if "?" in redirect_uri else "?"
auth_url = f"{redirect_uri}{separator}code={auth_code}"
if params.state:
auth_url += f"&state={params.state}"
return auth_url
async def load_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: str
) -> Optional[BasicMemoryAuthorizationCode]:
"""Load an authorization code."""
code = self.authorization_codes.get(authorization_code)
if code and code.client_id == client.client_id:
# Check if expired
if datetime.now(timezone.utc).timestamp() > code.expires_at:
del self.authorization_codes[authorization_code]
return None
return code
return None
async def exchange_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: BasicMemoryAuthorizationCode
) -> OAuthToken:
"""Exchange an authorization code for tokens."""
# Generate tokens
access_token = self._generate_access_token(client.client_id, authorization_code.scopes)
refresh_token = secrets.token_urlsafe(32)
# Store tokens
expires_at = (datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()
self.access_tokens[access_token] = BasicMemoryAccessToken(
token=access_token,
client_id=client.client_id,
scopes=authorization_code.scopes,
expires_at=int(expires_at),
)
self.refresh_tokens[refresh_token] = BasicMemoryRefreshToken(
token=refresh_token,
client_id=client.client_id,
scopes=authorization_code.scopes,
)
# Remove used authorization code
del self.authorization_codes[authorization_code.code]
return OAuthToken(
access_token=access_token,
token_type="bearer",
expires_in=3600, # 1 hour
refresh_token=refresh_token,
scope=" ".join(authorization_code.scopes) if authorization_code.scopes else None,
)
async def load_refresh_token(
self, client: OAuthClientInformationFull, refresh_token: str
) -> Optional[BasicMemoryRefreshToken]:
"""Load a refresh token."""
token = self.refresh_tokens.get(refresh_token)
if token and token.client_id == client.client_id:
return token
return None
async def exchange_refresh_token(
self,
client: OAuthClientInformationFull,
refresh_token: BasicMemoryRefreshToken,
scopes: list[str],
) -> OAuthToken:
"""Exchange a refresh token for new tokens."""
# Use requested scopes or original scopes
token_scopes = scopes if scopes else refresh_token.scopes
# Generate new tokens
new_access_token = self._generate_access_token(client.client_id, token_scopes)
new_refresh_token = secrets.token_urlsafe(32)
# Store new tokens
expires_at = (datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()
self.access_tokens[new_access_token] = BasicMemoryAccessToken(
token=new_access_token,
client_id=client.client_id,
scopes=token_scopes,
expires_at=int(expires_at),
)
self.refresh_tokens[new_refresh_token] = BasicMemoryRefreshToken(
token=new_refresh_token,
client_id=client.client_id,
scopes=token_scopes,
)
# Remove old tokens
del self.refresh_tokens[refresh_token.token]
return OAuthToken(
access_token=new_access_token,
token_type="bearer",
expires_in=3600, # 1 hour
refresh_token=new_refresh_token,
scope=" ".join(token_scopes) if token_scopes else None,
)
async def load_access_token(self, token: str) -> Optional[BasicMemoryAccessToken]:
"""Load and validate an access token."""
logger.debug("Loading access token, checking in-memory store first")
access_token = self.access_tokens.get(token)
if access_token:
# Check if expired
if access_token.expires_at and datetime.now(timezone.utc).timestamp() > access_token.expires_at:
logger.debug("Token found in memory but expired, removing")
del self.access_tokens[token]
return None
logger.debug("Token found in memory and valid")
return access_token
# Try to decode as JWT
logger.debug("Token not in memory, attempting JWT decode with secret key")
try:
# Decode with audience verification - PyJWT expects the audience to match
payload = jwt.decode(
token,
self.secret_key,
algorithms=["HS256"],
audience="basic-memory", # Expecting this audience
issuer=self.issuer_url, # And this issuer
)
logger.debug(f"JWT decoded successfully: {payload}")
return BasicMemoryAccessToken(
token=token,
client_id=payload.get("sub", ""),
scopes=payload.get("scopes", []),
expires_at=payload.get("exp"),
)
except jwt.InvalidTokenError as e:
logger.error(f"JWT decode failed: {e}")
return None
async def revoke_token(self, token: BasicMemoryAccessToken | BasicMemoryRefreshToken) -> None:
"""Revoke an access or refresh token."""
if isinstance(token, BasicMemoryAccessToken):
self.access_tokens.pop(token.token, None)
else:
self.refresh_tokens.pop(token.token, None)
def _generate_access_token(self, client_id: str, scopes: list[str]) -> str:
"""Generate a JWT access token."""
payload = {
"iss": self.issuer_url,
"sub": client_id,
"aud": "basic-memory",
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
"iat": datetime.now(timezone.utc),
"scopes": scopes,
}
return jwt.encode(payload, self.secret_key, algorithm="HS256")
@@ -1,321 +0,0 @@
"""External OAuth provider integration for Basic Memory MCP server."""
import os
from typing import Optional, Dict, Any
from dataclasses import dataclass
import httpx
from loguru import logger
from mcp.server.auth.provider import (
OAuthAuthorizationServerProvider,
AuthorizationParams,
AuthorizationCode,
RefreshToken,
AccessToken,
construct_redirect_uri,
)
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
@dataclass
class ExternalAuthorizationCode(AuthorizationCode):
"""Authorization code with external provider metadata."""
external_code: Optional[str] = None
state: Optional[str] = None
@dataclass
class ExternalRefreshToken(RefreshToken):
"""Refresh token with external provider metadata."""
external_token: Optional[str] = None
@dataclass
class ExternalAccessToken(AccessToken):
"""Access token with external provider metadata."""
external_token: Optional[str] = None
class ExternalOAuthProvider(
OAuthAuthorizationServerProvider[
ExternalAuthorizationCode, ExternalRefreshToken, ExternalAccessToken
]
):
"""OAuth provider that delegates to external OAuth providers.
This provider can integrate with services like:
- GitHub OAuth
- Google OAuth
- Auth0
- Okta
"""
def __init__(
self,
issuer_url: str,
external_provider: str,
external_client_id: str,
external_client_secret: str,
external_authorize_url: str,
external_token_url: str,
external_userinfo_url: Optional[str] = None,
):
self.issuer_url = issuer_url
self.external_provider = external_provider
self.external_client_id = external_client_id
self.external_client_secret = external_client_secret
self.external_authorize_url = external_authorize_url
self.external_token_url = external_token_url
self.external_userinfo_url = external_userinfo_url
# In-memory storage - in production, use a database
self.clients: Dict[str, OAuthClientInformationFull] = {}
self.codes: Dict[str, ExternalAuthorizationCode] = {}
self.tokens: Dict[str, Any] = {}
self.http_client = httpx.AsyncClient()
async def get_client(self, client_id: str) -> Optional[OAuthClientInformationFull]:
"""Get a client by ID."""
return self.clients.get(client_id)
async def register_client(self, client_info: OAuthClientInformationFull) -> None:
"""Register a new OAuth client."""
self.clients[client_info.client_id] = client_info
logger.info(f"Registered external OAuth client: {client_info.client_id}")
async def authorize(
self, client: OAuthClientInformationFull, params: AuthorizationParams
) -> str:
"""Create authorization URL redirecting to external provider."""
# Store authorization request
import secrets
state = secrets.token_urlsafe(32)
self.codes[state] = ExternalAuthorizationCode(
code=state,
scopes=params.scopes or [],
expires_at=0, # Will be set by external provider
client_id=client.client_id,
code_challenge=params.code_challenge,
redirect_uri=params.redirect_uri,
redirect_uri_provided_explicitly=params.redirect_uri_provided_explicitly,
state=params.state,
)
# Build external provider URL
external_params = {
"client_id": self.external_client_id,
"redirect_uri": f"{self.issuer_url}/callback",
"response_type": "code",
"state": state,
"scope": " ".join(params.scopes or []),
}
return construct_redirect_uri(self.external_authorize_url, **external_params)
async def handle_callback(self, code: str, state: str) -> str:
"""Handle callback from external provider."""
# Get original authorization request
auth_code = self.codes.get(state)
if not auth_code:
raise ValueError("Invalid state parameter")
# Exchange code with external provider
token_data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": f"{self.issuer_url}/callback",
"client_id": self.external_client_id,
"client_secret": self.external_client_secret,
}
response = await self.http_client.post(
self.external_token_url,
data=token_data,
)
response.raise_for_status()
external_tokens = response.json()
# Store external tokens
import secrets
internal_code = secrets.token_urlsafe(32)
self.codes[internal_code] = ExternalAuthorizationCode(
code=internal_code,
scopes=auth_code.scopes,
expires_at=0,
client_id=auth_code.client_id,
code_challenge=auth_code.code_challenge,
redirect_uri=auth_code.redirect_uri,
redirect_uri_provided_explicitly=auth_code.redirect_uri_provided_explicitly,
external_code=code,
state=auth_code.state,
)
self.tokens[internal_code] = external_tokens
# Redirect to original client
return construct_redirect_uri(
str(auth_code.redirect_uri),
code=internal_code,
state=auth_code.state,
)
async def load_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: str
) -> Optional[ExternalAuthorizationCode]:
"""Load an authorization code."""
code = self.codes.get(authorization_code)
if code and code.client_id == client.client_id:
return code
return None
async def exchange_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: ExternalAuthorizationCode
) -> OAuthToken:
"""Exchange authorization code for tokens."""
# Get stored external tokens
external_tokens = self.tokens.get(authorization_code.code)
if not external_tokens:
raise ValueError("No tokens found for authorization code")
# Map external tokens to MCP tokens
access_token = external_tokens.get("access_token")
refresh_token = external_tokens.get("refresh_token")
expires_in = external_tokens.get("expires_in", 3600)
# Store the mapping
self.tokens[access_token] = {
"client_id": client.client_id,
"external_token": access_token,
"scopes": authorization_code.scopes,
}
if refresh_token:
self.tokens[refresh_token] = {
"client_id": client.client_id,
"external_token": refresh_token,
"scopes": authorization_code.scopes,
}
# Clean up authorization code
del self.codes[authorization_code.code]
return OAuthToken(
access_token=access_token,
token_type="bearer",
expires_in=expires_in,
refresh_token=refresh_token,
scope=" ".join(authorization_code.scopes) if authorization_code.scopes else None,
)
async def load_refresh_token(
self, client: OAuthClientInformationFull, refresh_token: str
) -> Optional[ExternalRefreshToken]:
"""Load a refresh token."""
token_info = self.tokens.get(refresh_token)
if token_info and token_info["client_id"] == client.client_id:
return ExternalRefreshToken(
token=refresh_token,
client_id=client.client_id,
scopes=token_info["scopes"],
external_token=token_info.get("external_token"),
)
return None
async def exchange_refresh_token(
self,
client: OAuthClientInformationFull,
refresh_token: ExternalRefreshToken,
scopes: list[str],
) -> OAuthToken:
"""Exchange refresh token for new tokens."""
# Exchange with external provider
token_data = {
"grant_type": "refresh_token",
"refresh_token": refresh_token.external_token or refresh_token.token,
"client_id": self.external_client_id,
"client_secret": self.external_client_secret,
}
response = await self.http_client.post(
self.external_token_url,
data=token_data,
)
response.raise_for_status()
external_tokens = response.json()
# Update stored tokens
new_access_token = external_tokens.get("access_token")
new_refresh_token = external_tokens.get("refresh_token", refresh_token.token)
expires_in = external_tokens.get("expires_in", 3600)
self.tokens[new_access_token] = {
"client_id": client.client_id,
"external_token": new_access_token,
"scopes": scopes or refresh_token.scopes,
}
if new_refresh_token != refresh_token.token:
self.tokens[new_refresh_token] = {
"client_id": client.client_id,
"external_token": new_refresh_token,
"scopes": scopes or refresh_token.scopes,
}
del self.tokens[refresh_token.token]
return OAuthToken(
access_token=new_access_token,
token_type="bearer",
expires_in=expires_in,
refresh_token=new_refresh_token,
scope=" ".join(scopes or refresh_token.scopes),
)
async def load_access_token(self, token: str) -> Optional[ExternalAccessToken]:
"""Load and validate an access token."""
token_info = self.tokens.get(token)
if token_info:
return ExternalAccessToken(
token=token,
client_id=token_info["client_id"],
scopes=token_info["scopes"],
external_token=token_info.get("external_token"),
)
return None
async def revoke_token(self, token: ExternalAccessToken | ExternalRefreshToken) -> None:
"""Revoke a token."""
self.tokens.pop(token.token, None)
def create_github_provider() -> ExternalOAuthProvider:
"""Create an OAuth provider for GitHub integration."""
return ExternalOAuthProvider(
issuer_url=os.getenv("FASTMCP_AUTH_ISSUER_URL", "http://localhost:8000"),
external_provider="github",
external_client_id=os.getenv("GITHUB_CLIENT_ID", ""),
external_client_secret=os.getenv("GITHUB_CLIENT_SECRET", ""),
external_authorize_url="https://github.com/login/oauth/authorize",
external_token_url="https://github.com/login/oauth/access_token",
external_userinfo_url="https://api.github.com/user",
)
def create_google_provider() -> ExternalOAuthProvider:
"""Create an OAuth provider for Google integration."""
return ExternalOAuthProvider(
issuer_url=os.getenv("FASTMCP_AUTH_ISSUER_URL", "http://localhost:8000"),
external_provider="google",
external_client_id=os.getenv("GOOGLE_CLIENT_ID", ""),
external_client_secret=os.getenv("GOOGLE_CLIENT_SECRET", ""),
external_authorize_url="https://accounts.google.com/o/oauth2/v2/auth",
external_token_url="https://oauth2.googleapis.com/token",
external_userinfo_url="https://www.googleapis.com/oauth2/v1/userinfo",
)
+6 -4
View File
@@ -8,7 +8,7 @@ from dataclasses import dataclass
from typing import Optional
from loguru import logger
from basic_memory.config import ProjectConfig, get_project_config, config_manager
from basic_memory.config import ProjectConfig, get_project_config, ConfigManager
@dataclass
@@ -23,7 +23,7 @@ class ProjectSession:
current_project: Optional[str] = None
default_project: Optional[str] = None
def initialize(self, default_project: str) -> None:
def initialize(self, default_project: str) -> "ProjectSession":
"""Set the default project from config on startup.
Args:
@@ -32,6 +32,7 @@ class ProjectSession:
self.default_project = default_project
self.current_project = default_project
logger.info(f"Initialized project session with default project: {default_project}")
return self
def get_current_project(self) -> str:
"""Get the currently active project name.
@@ -72,7 +73,7 @@ class ProjectSession:
via CLI or API to ensure MCP session stays in sync.
"""
# Reload config to get latest default project
current_config = config_manager.load_config()
current_config = ConfigManager().config
new_default = current_config.default_project
# Reinitialize with new default
@@ -102,7 +103,8 @@ def get_active_project(project_override: Optional[str] = None) -> ProjectConfig:
return project
current_project = session.get_current_project()
return get_project_config(current_project)
active_project = get_project_config(current_project)
return active_project
def add_project_metadata(result: str, project_name: str) -> str:
-2
View File
@@ -10,12 +10,10 @@ from basic_memory.mcp.prompts import continue_conversation
from basic_memory.mcp.prompts import recent_activity
from basic_memory.mcp.prompts import search
from basic_memory.mcp.prompts import ai_assistant_guide
from basic_memory.mcp.prompts import sync_status
__all__ = [
"ai_assistant_guide",
"continue_conversation",
"recent_activity",
"search",
"sync_status",
]
-112
View File
@@ -1,112 +0,0 @@
"""Sync status prompt for Basic Memory MCP server."""
from basic_memory.mcp.server import mcp
@mcp.prompt(
description="""Get sync status with recommendations for AI assistants.
This prompt provides both current sync status and guidance on how
AI assistants should respond when sync operations are in progress or completed.
""",
)
async def sync_status_prompt() -> str:
"""Get sync status with AI assistant guidance.
Returns:
Formatted sync status with AI assistant guidance
"""
try: # pragma: no cover
from basic_memory.services.migration_service import migration_manager
state = migration_manager.state
# Build status report
lines = [
"# Basic Memory Sync Status",
"",
f"**Current Status**: {state.status.value.replace('_', ' ').title()}",
f"**System Ready**: {'Yes' if migration_manager.is_ready else 'No'}",
"",
]
if migration_manager.is_ready:
lines.extend(
[
"✅ **All sync operations completed** - System is fully operational",
"",
"All Basic Memory tools are available and functioning normally.",
"File indexing is complete and knowledge graphs are up to date.",
"You can proceed with any knowledge management tasks.",
]
)
else:
lines.append(f"**Status Message**: {state.message}")
if state.status.value == "in_progress":
if state.projects_total > 0:
progress = f" ({state.projects_migrated}/{state.projects_total})"
lines.append(f"**Progress**: {progress}")
lines.extend(
[
"",
"🔄 **File synchronization in progress** - Processing files and building knowledge graphs",
"",
"**Impact**: Some tools may show status messages instead of normal responses",
"until sync completes (usually 1-3 minutes).",
]
)
elif state.status.value == "failed":
lines.extend(
[
"",
f"❌ **Sync failed**: {state.error or 'Unknown error'}",
"",
"**Impact**: System may have limited functionality until issue is resolved.",
]
)
# Add AI assistant recommendations
if not migration_manager.is_ready:
lines.extend(
[
"",
"---",
"",
"## AI Assistant Recommendations",
"",
"**When sync is in progress:**",
"- Inform the user about the background file processing",
"- Suggest using `sync_status()` tool to check progress",
"- Explain that tools will work normally once sync completes",
"- Avoid creating complex workflows until sync is done",
"",
"**What to tell users:**",
"- 'Basic Memory is processing your files and building knowledge graphs'",
"- 'This usually takes 1-3 minutes depending on your content size'",
"- 'You can check progress anytime with the sync_status tool'",
"- 'Full functionality will be available once processing completes'",
"",
"**User-friendly language:**",
"- Say 'processing files' instead of 'migration' or 'sync'",
"- Say 'building knowledge graphs' instead of 'indexing'",
"- Say 'setting up your knowledge base' instead of 'running migrations'",
]
)
return "\n".join(lines)
except Exception as e: # pragma: no cover
return f"""# Sync Status - Error
❌ **Unable to check sync status**: {str(e)}
## AI Assistant Recommendations
**When status is unavailable:**
- Assume the system is likely working normally
- Try proceeding with normal operations
- If users report issues, suggest checking logs or restarting
- Use user-friendly language about 'setting up the knowledge base'
"""
+8 -71
View File
@@ -7,25 +7,10 @@ from contextlib import asynccontextmanager
from dataclasses import dataclass
from typing import AsyncIterator, Optional, Any
from dotenv import load_dotenv
from fastmcp import FastMCP
from fastmcp.utilities.logging import configure_logging as mcp_configure_logging
from mcp.server.auth.settings import AuthSettings
from basic_memory.config import app_config
from basic_memory.config import ConfigManager
from basic_memory.services.initialization import initialize_app
from basic_memory.mcp.auth_provider import BasicMemoryOAuthProvider
from basic_memory.mcp.project_session import session
from basic_memory.mcp.external_auth_provider import (
create_github_provider,
create_google_provider,
)
from basic_memory.mcp.supabase_auth_provider import SupabaseOAuthProvider
# mcp console logging
mcp_configure_logging(level="ERROR")
load_dotenv()
@dataclass
@@ -36,7 +21,11 @@ class AppContext:
@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pragma: no cover
"""Manage application lifecycle with type-safe context"""
""" """
# defer import so tests can monkeypatch
from basic_memory.mcp.project_session import session
app_config = ConfigManager().config
# Initialize on startup (now returns migration_manager)
migration_manager = await initialize_app(app_config)
@@ -50,60 +39,8 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: # pragma:
pass
# OAuth configuration function
def create_auth_config() -> tuple[AuthSettings | None, Any | None]:
"""Create OAuth configuration if enabled."""
# Check if OAuth is enabled via environment variable
import os
if os.getenv("FASTMCP_AUTH_ENABLED", "false").lower() == "true":
from pydantic import AnyHttpUrl
# Configure OAuth settings
issuer_url = os.getenv("FASTMCP_AUTH_ISSUER_URL", "http://localhost:8000")
required_scopes = os.getenv("FASTMCP_AUTH_REQUIRED_SCOPES", "read,write")
docs_url = os.getenv("FASTMCP_AUTH_DOCS_URL") or "http://localhost:8000/docs/oauth"
auth_settings = AuthSettings(
issuer_url=AnyHttpUrl(issuer_url),
service_documentation_url=AnyHttpUrl(docs_url),
required_scopes=required_scopes.split(",") if required_scopes else ["read", "write"],
)
# Create OAuth provider based on type
provider_type = os.getenv("FASTMCP_AUTH_PROVIDER", "basic").lower()
if provider_type == "github":
auth_provider = create_github_provider()
elif provider_type == "google":
auth_provider = create_google_provider()
elif provider_type == "supabase":
supabase_url = os.getenv("SUPABASE_URL")
supabase_anon_key = os.getenv("SUPABASE_ANON_KEY")
supabase_service_key = os.getenv("SUPABASE_SERVICE_KEY")
if not supabase_url or not supabase_anon_key:
raise ValueError("SUPABASE_URL and SUPABASE_ANON_KEY must be set for Supabase auth")
auth_provider = SupabaseOAuthProvider(
supabase_url=supabase_url,
supabase_anon_key=supabase_anon_key,
supabase_service_key=supabase_service_key,
issuer_url=issuer_url,
)
else: # default to "basic"
auth_provider = BasicMemoryOAuthProvider(issuer_url=issuer_url)
return auth_settings, auth_provider
return None, None
# Create auth configuration
auth_settings, auth_provider = create_auth_config()
# Create the shared server instance
# Create the shared server instance with custom Stytch auth
mcp = FastMCP(
name="Basic Memory",
auth=auth_provider,
lifespan=app_lifespan,
)
@@ -1,463 +0,0 @@
"""Supabase OAuth provider for Basic Memory MCP server."""
import os
import secrets
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from typing import Optional, Dict, Any
import httpx
import jwt
from loguru import logger
from mcp.server.auth.provider import (
OAuthAuthorizationServerProvider,
AuthorizationParams,
AuthorizationCode,
RefreshToken,
AccessToken,
TokenError,
AuthorizeError,
)
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
@dataclass
class SupabaseAuthorizationCode(AuthorizationCode):
"""Authorization code with Supabase metadata."""
user_id: Optional[str] = None
email: Optional[str] = None
@dataclass
class SupabaseRefreshToken(RefreshToken):
"""Refresh token with Supabase metadata."""
supabase_refresh_token: Optional[str] = None
user_id: Optional[str] = None
@dataclass
class SupabaseAccessToken(AccessToken):
"""Access token with Supabase metadata."""
supabase_access_token: Optional[str] = None
user_id: Optional[str] = None
email: Optional[str] = None
class SupabaseOAuthProvider(
OAuthAuthorizationServerProvider[
SupabaseAuthorizationCode, SupabaseRefreshToken, SupabaseAccessToken
]
):
"""OAuth provider that integrates with Supabase Auth.
This provider uses Supabase as the authentication backend while
maintaining compatibility with MCP's OAuth requirements.
"""
def __init__(
self,
supabase_url: str,
supabase_anon_key: str,
supabase_service_key: Optional[str] = None,
issuer_url: str = "http://localhost:8000",
):
self.supabase_url = supabase_url.rstrip("/")
self.supabase_anon_key = supabase_anon_key
self.supabase_service_key = supabase_service_key or supabase_anon_key
self.issuer_url = issuer_url
# HTTP client for Supabase API calls
self.http_client = httpx.AsyncClient()
# Temporary storage for auth flows (in production, use Supabase DB)
self.pending_auth_codes: Dict[str, SupabaseAuthorizationCode] = {}
self.mcp_to_supabase_tokens: Dict[str, Dict[str, Any]] = {}
async def get_client(self, client_id: str) -> Optional[OAuthClientInformationFull]:
"""Get a client from Supabase.
In production, this would query a clients table in Supabase.
"""
# For now, we'll validate against a configured list of allowed clients
# In production, query Supabase DB for client info
allowed_clients = os.getenv("SUPABASE_ALLOWED_CLIENTS", "").split(",")
if client_id in allowed_clients:
return OAuthClientInformationFull(
client_id=client_id,
client_secret="", # Supabase handles secrets
redirect_uris=[], # Supabase handles redirect URIs
)
return None
async def register_client(self, client_info: OAuthClientInformationFull) -> None:
"""Register a new OAuth client in Supabase.
In production, this would insert into a clients table.
"""
# For development, we just log the registration
logger.info(f"Would register client {client_info.client_id} in Supabase")
# In production:
# await self.supabase.table('oauth_clients').insert({
# 'client_id': client_info.client_id,
# 'client_secret': client_info.client_secret,
# 'metadata': client_info.client_metadata,
# }).execute()
async def authorize(
self, client: OAuthClientInformationFull, params: AuthorizationParams
) -> str:
"""Create authorization URL redirecting to Supabase Auth.
This initiates the OAuth flow with Supabase as the identity provider.
"""
# Generate state for this auth request
state = secrets.token_urlsafe(32)
# Store the authorization request
self.pending_auth_codes[state] = SupabaseAuthorizationCode(
code=state,
scopes=params.scopes or [],
expires_at=(datetime.now(timezone.utc) + timedelta(minutes=10)).timestamp(),
client_id=client.client_id,
code_challenge=params.code_challenge,
redirect_uri=params.redirect_uri,
redirect_uri_provided_explicitly=params.redirect_uri_provided_explicitly,
)
# Build Supabase auth URL
auth_params = {
"redirect_to": f"{self.issuer_url}/auth/callback",
"scopes": " ".join(params.scopes or ["openid", "email"]),
"state": state,
}
# Use Supabase's OAuth endpoint
auth_url = f"{self.supabase_url}/auth/v1/authorize"
query_string = "&".join(f"{k}={v}" for k, v in auth_params.items())
return f"{auth_url}?{query_string}"
async def handle_supabase_callback(self, code: str, state: str) -> str:
"""Handle callback from Supabase after user authentication."""
# Get the original auth request
auth_request = self.pending_auth_codes.get(state)
if not auth_request:
raise AuthorizeError(
error="invalid_request",
error_description="Invalid state parameter",
)
# Exchange code with Supabase for tokens
token_response = await self.http_client.post(
f"{self.supabase_url}/auth/v1/token",
json={
"grant_type": "authorization_code",
"code": code,
"redirect_uri": f"{self.issuer_url}/auth/callback",
},
headers={
"apikey": self.supabase_anon_key,
"Authorization": f"Bearer {self.supabase_anon_key}",
},
)
if not token_response.is_success:
raise AuthorizeError(
error="server_error",
error_description="Failed to exchange code with Supabase",
)
supabase_tokens = token_response.json()
# Get user info from Supabase
user_response = await self.http_client.get(
f"{self.supabase_url}/auth/v1/user",
headers={
"apikey": self.supabase_anon_key,
"Authorization": f"Bearer {supabase_tokens['access_token']}",
},
)
user_data = user_response.json() if user_response.is_success else {}
# Generate MCP authorization code
mcp_code = secrets.token_urlsafe(32)
# Update auth request with user info
auth_request.code = mcp_code
auth_request.user_id = user_data.get("id")
auth_request.email = user_data.get("email")
# Store mapping
self.pending_auth_codes[mcp_code] = auth_request
self.mcp_to_supabase_tokens[mcp_code] = {
"supabase_tokens": supabase_tokens,
"user": user_data,
}
# Clean up old state
del self.pending_auth_codes[state]
# Redirect back to client
redirect_uri = str(auth_request.redirect_uri)
separator = "&" if "?" in redirect_uri else "?"
return f"{redirect_uri}{separator}code={mcp_code}&state={state}"
async def load_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: str
) -> Optional[SupabaseAuthorizationCode]:
"""Load an authorization code."""
code = self.pending_auth_codes.get(authorization_code)
if code and code.client_id == client.client_id:
# Check expiration
if datetime.now(timezone.utc).timestamp() > code.expires_at:
del self.pending_auth_codes[authorization_code]
return None
return code
return None
async def exchange_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: SupabaseAuthorizationCode
) -> OAuthToken:
"""Exchange authorization code for tokens."""
# Get stored Supabase tokens
token_data = self.mcp_to_supabase_tokens.get(authorization_code.code)
if not token_data:
raise TokenError(error="invalid_grant", error_description="Invalid authorization code")
supabase_tokens = token_data["supabase_tokens"]
user = token_data["user"]
# Generate MCP tokens that wrap Supabase tokens
access_token = self._generate_mcp_token(
client_id=client.client_id,
user_id=user.get("id", ""),
email=user.get("email", ""),
scopes=authorization_code.scopes,
supabase_access_token=supabase_tokens["access_token"],
)
refresh_token = secrets.token_urlsafe(32)
# Store the token mapping
self.mcp_to_supabase_tokens[access_token] = {
"client_id": client.client_id,
"user_id": user.get("id"),
"email": user.get("email"),
"supabase_access_token": supabase_tokens["access_token"],
"supabase_refresh_token": supabase_tokens["refresh_token"],
"scopes": authorization_code.scopes,
}
# Store refresh token mapping
self.mcp_to_supabase_tokens[refresh_token] = {
"client_id": client.client_id,
"user_id": user.get("id"),
"supabase_refresh_token": supabase_tokens["refresh_token"],
"scopes": authorization_code.scopes,
}
# Clean up authorization code
del self.pending_auth_codes[authorization_code.code]
del self.mcp_to_supabase_tokens[authorization_code.code]
return OAuthToken(
access_token=access_token,
token_type="bearer",
expires_in=supabase_tokens.get("expires_in", 3600),
refresh_token=refresh_token,
scope=" ".join(authorization_code.scopes) if authorization_code.scopes else None,
)
async def load_refresh_token(
self, client: OAuthClientInformationFull, refresh_token: str
) -> Optional[SupabaseRefreshToken]:
"""Load a refresh token."""
token_data = self.mcp_to_supabase_tokens.get(refresh_token)
if token_data and token_data["client_id"] == client.client_id:
return SupabaseRefreshToken(
token=refresh_token,
client_id=client.client_id,
scopes=token_data["scopes"],
supabase_refresh_token=token_data["supabase_refresh_token"],
user_id=token_data.get("user_id"),
)
return None
async def exchange_refresh_token(
self,
client: OAuthClientInformationFull,
refresh_token: SupabaseRefreshToken,
scopes: list[str],
) -> OAuthToken:
"""Exchange refresh token for new tokens using Supabase."""
# Refresh with Supabase
token_response = await self.http_client.post(
f"{self.supabase_url}/auth/v1/token",
json={
"grant_type": "refresh_token",
"refresh_token": refresh_token.supabase_refresh_token,
},
headers={
"apikey": self.supabase_anon_key,
"Authorization": f"Bearer {self.supabase_anon_key}",
},
)
if not token_response.is_success:
raise TokenError(
error="invalid_grant",
error_description="Failed to refresh with Supabase",
)
supabase_tokens = token_response.json()
# Get updated user info
user_response = await self.http_client.get(
f"{self.supabase_url}/auth/v1/user",
headers={
"apikey": self.supabase_anon_key,
"Authorization": f"Bearer {supabase_tokens['access_token']}",
},
)
user_data = user_response.json() if user_response.is_success else {}
# Generate new MCP tokens
new_access_token = self._generate_mcp_token(
client_id=client.client_id,
user_id=user_data.get("id", ""),
email=user_data.get("email", ""),
scopes=scopes or refresh_token.scopes,
supabase_access_token=supabase_tokens["access_token"],
)
new_refresh_token = secrets.token_urlsafe(32)
# Update token mappings
self.mcp_to_supabase_tokens[new_access_token] = {
"client_id": client.client_id,
"user_id": user_data.get("id"),
"email": user_data.get("email"),
"supabase_access_token": supabase_tokens["access_token"],
"supabase_refresh_token": supabase_tokens["refresh_token"],
"scopes": scopes or refresh_token.scopes,
}
self.mcp_to_supabase_tokens[new_refresh_token] = {
"client_id": client.client_id,
"user_id": user_data.get("id"),
"supabase_refresh_token": supabase_tokens["refresh_token"],
"scopes": scopes or refresh_token.scopes,
}
# Clean up old tokens
del self.mcp_to_supabase_tokens[refresh_token.token]
return OAuthToken(
access_token=new_access_token,
token_type="bearer",
expires_in=supabase_tokens.get("expires_in", 3600),
refresh_token=new_refresh_token,
scope=" ".join(scopes or refresh_token.scopes),
)
async def load_access_token(self, token: str) -> Optional[SupabaseAccessToken]:
"""Load and validate an access token."""
# First check our mapping
token_data = self.mcp_to_supabase_tokens.get(token)
if token_data:
return SupabaseAccessToken(
token=token,
client_id=token_data["client_id"],
scopes=token_data["scopes"],
supabase_access_token=token_data.get("supabase_access_token"),
user_id=token_data.get("user_id"),
email=token_data.get("email"),
)
# Try to decode as JWT
try:
# Verify with Supabase's JWT secret
payload = jwt.decode(
token,
os.getenv("SUPABASE_JWT_SECRET", ""),
algorithms=["HS256"],
audience="authenticated",
)
return SupabaseAccessToken(
token=token,
client_id=payload.get("client_id", ""),
scopes=payload.get("scopes", []),
user_id=payload.get("sub"),
email=payload.get("email"),
)
except jwt.InvalidTokenError:
pass
# Validate with Supabase
user_response = await self.http_client.get(
f"{self.supabase_url}/auth/v1/user",
headers={
"apikey": self.supabase_anon_key,
"Authorization": f"Bearer {token}",
},
)
if user_response.is_success:
user_data = user_response.json()
return SupabaseAccessToken(
token=token,
client_id="", # Unknown client for direct Supabase tokens
scopes=[],
supabase_access_token=token,
user_id=user_data.get("id"),
email=user_data.get("email"),
)
return None
async def revoke_token(self, token: SupabaseAccessToken | SupabaseRefreshToken) -> None:
"""Revoke a token."""
# Remove from our mapping
self.mcp_to_supabase_tokens.pop(token.token, None)
# In production, also revoke in Supabase:
# await self.supabase.auth.admin.sign_out(token.user_id)
def _generate_mcp_token(
self,
client_id: str,
user_id: str,
email: str,
scopes: list[str],
supabase_access_token: str,
) -> str:
"""Generate an MCP token that wraps Supabase authentication."""
payload = {
"iss": self.issuer_url,
"sub": user_id,
"client_id": client_id,
"email": email,
"scopes": scopes,
"supabase_token": supabase_access_token[:10] + "...", # Reference only
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
"iat": datetime.now(timezone.utc),
}
# Use Supabase JWT secret if available
secret = os.getenv("SUPABASE_JWT_SECRET", secrets.token_urlsafe(32))
return jwt.encode(payload, secret, algorithm="HS256")
+12 -2
View File
@@ -15,6 +15,7 @@ from basic_memory.schemas.memory import (
memory_url_path,
)
type StringOrInt = str | int
@mcp.tool(
description="""Build context from a memory:// URI to continue conversations naturally.
@@ -35,7 +36,7 @@ from basic_memory.schemas.memory import (
)
async def build_context(
url: MemoryUrl,
depth: Optional[int] = 1,
depth: Optional[StringOrInt] = 1,
timeframe: Optional[TimeFrame] = "7d",
page: int = 1,
page_size: int = 10,
@@ -80,6 +81,15 @@ async def build_context(
build_context("memory://specs/search", project="work-project")
"""
logger.info(f"Building context from {url}")
# Convert string depth to integer if needed
if isinstance(depth, str):
try:
depth = int(depth)
except ValueError:
from mcp.server.fastmcp.exceptions import ToolError
raise ToolError(f"Invalid depth parameter: '{depth}' is not a valid integer")
# URL is already validated and normalized by MemoryUrl type annotation
# Get the active project first to check project-specific sync status
@@ -101,7 +111,7 @@ async def build_context(
metadata=MemoryMetadata(
depth=depth or 1,
timeframe=timeframe,
generated_at=datetime.now(),
generated_at=datetime.now().astimezone(),
primary_count=0,
related_count=0,
uri=migration_status, # Include status in metadata
+24 -12
View File
@@ -11,6 +11,7 @@ from basic_memory.mcp.tools.utils import call_post, call_get
from basic_memory.mcp.project_session import get_active_project
from basic_memory.schemas import EntityResponse
from basic_memory.schemas.project_info import ProjectList
from basic_memory.utils import validate_project_path
async def _detect_cross_project_move_attempt(
@@ -47,18 +48,7 @@ async def _detect_cross_project_move_attempt(
identifier, destination_path, current_project, matching_project
)
# Check if the destination path looks like it might be trying to reference another project
# (e.g., contains common project-like patterns)
if any(keyword in dest_lower for keyword in ["project", "workspace", "repo"]):
# This might be a cross-project attempt, but we can't be sure
# Return a general guidance message
available_projects = [
p.name for p in project_list.projects if p.name != current_project
]
if available_projects:
return _format_potential_cross_project_guidance(
identifier, destination_path, current_project, available_projects
)
# No other cross-project patterns detected
except Exception as e:
# If we can't detect, don't interfere with normal error handling
@@ -404,6 +394,28 @@ async def move_note(
active_project = get_active_project(project)
project_url = active_project.project_url
# Validate destination path to prevent path traversal attacks
project_path = active_project.home
if not validate_project_path(destination_path, project_path):
logger.warning(
"Attempted path traversal attack blocked",
destination_path=destination_path,
project=active_project.name,
)
return f"""# Move Failed - Security Validation Error
The destination path '{destination_path}' is not allowed - paths must stay within project boundaries.
## Valid path examples:
- `notes/my-file.md`
- `projects/2025/meeting-notes.md`
- `archive/old-notes.md`
## Try again with a safe path:
```
move_note("{identifier}", "notes/{destination_path.split("/")[-1] if "/" in destination_path else destination_path}")
```"""
# Check for potential cross-project move attempts
cross_project_error = await _detect_cross_project_move_attempt(
identifier, destination_path, active_project.name
@@ -221,8 +221,10 @@ async def set_default_project(project_name: str, ctx: Context | None = None) ->
if ctx: # pragma: no cover
await ctx.info(f"Setting default project to: {project_name}")
# Call API to set default project
response = await call_put(client, f"/projects/{project_name}/default")
# Call API to set default project using URL encoding for special characters
from urllib.parse import quote
encoded_name = quote(project_name, safe='')
response = await call_put(client, f"/projects/{encoded_name}/default")
status_response = ProjectStatusResponse.model_validate(response.json())
result = f"{status_response.message}\n\n"
@@ -323,16 +325,29 @@ async def delete_project(project_name: str, ctx: Context | None = None) -> str:
response = await call_get(client, "/projects/projects")
project_list = ProjectList.model_validate(response.json())
# Check if project exists
project_exists = any(p.name == project_name for p in project_list.projects)
if not project_exists:
# Find the project by name (case-insensitive) or permalink - same logic as switch_project
project_permalink = generate_permalink(project_name)
target_project = None
for p in project_list.projects:
# Match by permalink (handles case-insensitive input)
if p.permalink == project_permalink:
target_project = p
break
# Also match by name comparison (case-insensitive)
if p.name.lower() == project_name.lower():
target_project = p
break
if not target_project:
available_projects = [p.name for p in project_list.projects]
raise ValueError(
f"Project '{project_name}' not found. Available projects: {', '.join(available_projects)}"
)
# Call API to delete project
response = await call_delete(client, f"/projects/{project_name}")
# Call API to delete project using URL encoding for special characters
from urllib.parse import quote
encoded_name = quote(target_project.name, safe='')
response = await call_delete(client, f"/projects/{encoded_name}")
status_response = ProjectStatusResponse.model_validate(response.json())
result = f"{status_response.message}\n\n"
@@ -17,6 +17,7 @@ from basic_memory.mcp.async_client import client
from basic_memory.mcp.tools.utils import call_get
from basic_memory.mcp.project_session import get_active_project
from basic_memory.schemas.memory import memory_url_path
from basic_memory.utils import validate_project_path
def calculate_target_params(content_length):
@@ -188,6 +189,21 @@ async def read_content(path: str, project: Optional[str] = None) -> dict:
project_url = active_project.project_url
url = memory_url_path(path)
# Validate path to prevent path traversal attacks
project_path = active_project.home
if not validate_project_path(url, project_path):
logger.warning(
"Attempted path traversal attack blocked",
path=path,
url=url,
project=active_project.name,
)
return {
"type": "error",
"error": f"Path '{path}' is not allowed - paths must stay within project boundaries",
}
response = await call_get(client, f"{project_url}/resource/{url}")
content_type = response.headers.get("content-type", "application/octet-stream")
content_length = int(response.headers.get("content-length", 0))
+17 -2
View File
@@ -11,6 +11,7 @@ from basic_memory.mcp.tools.search import search_notes
from basic_memory.mcp.tools.utils import call_get
from basic_memory.mcp.project_session import get_active_project
from basic_memory.schemas.memory import memory_url_path
from basic_memory.utils import validate_project_path
@mcp.tool(
@@ -55,6 +56,20 @@ async def read_note(
# Get the active project first to check project-specific sync status
active_project = get_active_project(project)
# Validate identifier to prevent path traversal attacks
# We need to check both the raw identifier and the processed path
processed_path = memory_url_path(identifier)
project_path = active_project.home
if not validate_project_path(identifier, project_path) or not validate_project_path(processed_path, project_path):
logger.warning(
"Attempted path traversal attack blocked",
identifier=identifier,
processed_path=processed_path,
project=active_project.name,
)
return f"# Error\n\nIdentifier '{identifier}' is not allowed - paths must stay within project boundaries"
# Check migration status and wait briefly if needed
from basic_memory.mcp.tools.utils import wait_for_migration_or_return_status
@@ -124,7 +139,7 @@ def format_not_found_message(identifier: str) -> str:
return dedent(f"""
# Note Not Found: "{identifier}"
I searched for "{identifier}" using multiple methods (direct lookup, title search, and text search) but couldn't find any matching notes. Here are some suggestions:
I couldn't find any notes matching "{identifier}". Here are some suggestions:
## Check Identifier Type
- If you provided a title, try using the exact permalink instead
@@ -170,7 +185,7 @@ def format_related_results(identifier: str, results) -> str:
message = dedent(f"""
# Note Not Found: "{identifier}"
I searched for "{identifier}" using direct lookup and title search but couldn't find an exact match. However, I found some related notes through text search:
I couldn't find an exact match for "{identifier}", but I found some related notes:
""")
+3 -2
View File
@@ -4,8 +4,10 @@ from typing import Optional
from loguru import logger
from basic_memory.config import ConfigManager
from basic_memory.mcp.server import mcp
from basic_memory.mcp.project_session import get_active_project
from basic_memory.services.sync_status_service import sync_status_tracker
def _get_all_projects_status() -> list[str]:
@@ -13,8 +15,7 @@ def _get_all_projects_status() -> list[str]:
status_lines = []
try:
from basic_memory.config import app_config
from basic_memory.services.sync_status_service import sync_status_tracker
app_config = ConfigManager().config
if app_config.projects:
status_lines.extend(["", "---", "", "**All Projects Status:**"])
+9 -1
View File
@@ -10,7 +10,7 @@ from basic_memory.mcp.tools.utils import call_put
from basic_memory.mcp.project_session import get_active_project
from basic_memory.schemas import EntityResponse
from basic_memory.schemas.base import Entity
from basic_memory.utils import parse_tags
from basic_memory.utils import parse_tags, validate_project_path
# Define TagType as a Union that can accept either a string or a list of strings or None
TagType = Union[List[str], str, None]
@@ -75,6 +75,14 @@ async def write_note(
# Get the active project first to check project-specific sync status
active_project = get_active_project(project)
# Validate folder path to prevent path traversal attacks
project_path = active_project.home
if folder and not validate_project_path(folder, project_path):
logger.warning(
"Attempted path traversal attack blocked", folder=folder, project=active_project.name
)
return f"# Error\n\nFolder path '{folder}' is not allowed - paths must stay within project boundaries"
# Check migration status and wait briefly if needed
from basic_memory.mcp.tools.utils import wait_for_migration_or_return_status
+13 -2
View File
@@ -1,6 +1,7 @@
"""Knowledge graph models."""
from datetime import datetime
from basic_memory.utils import ensure_timezone_aware
from typing import Optional
from sqlalchemy import (
@@ -73,8 +74,8 @@ class Entity(Base):
checksum: Mapped[Optional[str]] = mapped_column(String, nullable=True)
# Metadata and tracking
created_at: Mapped[datetime] = mapped_column(DateTime)
updated_at: Mapped[datetime] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now().astimezone())
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now().astimezone(), onupdate=lambda: datetime.now().astimezone())
# Relationships
project = relationship("Project", back_populates="entities")
@@ -103,6 +104,16 @@ class Entity(Base):
def is_markdown(self):
"""Check if the entity is a markdown file."""
return self.content_type == "text/markdown"
def __getattribute__(self, name):
"""Override attribute access to ensure datetime fields are timezone-aware."""
value = super().__getattribute__(name)
# Ensure datetime fields are timezone-aware
if name in ('created_at', 'updated_at') and isinstance(value, datetime):
return ensure_timezone_aware(value)
return value
def __repr__(self) -> str:
return f"Entity(id={self.id}, name='{self.title}', type='{self.entity_type}'"
+3 -3
View File
@@ -1,6 +1,6 @@
"""Project model for Basic Memory."""
from datetime import datetime
from datetime import datetime, UTC
from typing import Optional
from sqlalchemy import (
@@ -52,9 +52,9 @@ class Project(Base):
is_default: Mapped[Optional[bool]] = mapped_column(Boolean, default=None, nullable=True)
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(UTC))
updated_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
DateTime(timezone=True), default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC)
)
# Define relationships to entities, observations, and relations
@@ -57,7 +57,7 @@ class EntityRepository(Repository[Entity]):
"""
query = (
self.select()
.where(Entity.file_path == str(file_path))
.where(Entity.file_path == Path(file_path).as_posix())
.options(*self.get_load_options())
)
return await self.find_one(query)
@@ -68,7 +68,7 @@ class EntityRepository(Repository[Entity]):
Args:
file_path: Path to the entity file (will be converted to string internally)
"""
return await self.delete_by_fields(file_path=str(file_path))
return await self.delete_by_fields(file_path=Path(file_path).as_posix())
def get_load_options(self) -> List[LoaderOption]:
"""Get SQLAlchemy loader options for eager loading relationships."""
@@ -46,7 +46,7 @@ class ProjectRepository(Repository[Project]):
Args:
path: Path to the project directory (will be converted to string internally)
"""
query = self.select().where(Project.path == str(path))
query = self.select().where(Project.path == Path(path).as_posix())
return await self.find_one(query)
async def get_default_project(self) -> Optional[Project]:
@@ -83,3 +83,21 @@ class ProjectRepository(Repository[Project]):
await session.flush()
return target_project
return None # pragma: no cover
async def update_path(self, project_id: int, new_path: str) -> Optional[Project]:
"""Update project path.
Args:
project_id: ID of the project to update
new_path: New filesystem path for the project
Returns:
The updated project if found, None otherwise
"""
async with db.scoped_session(self.session_maker) as session:
project = await self.select_by_id(session, project_id)
if project:
project.path = new_path
await session.flush()
return project
return None
@@ -6,6 +6,7 @@ import time
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, List, Optional
from pathlib import Path
from loguru import logger
from sqlalchemy import Executable, Result, text
@@ -59,8 +60,11 @@ class SearchIndexRow:
if not self.type == SearchItemType.ENTITY.value and not self.file_path:
return ""
# Normalize path separators to handle both Windows (\) and Unix (/) paths
normalized_path = Path(self.file_path).as_posix()
# Split the path by slashes
parts = self.file_path.split("/")
parts = normalized_path.split("/")
# If there's only one part (e.g., "README.md"), it's at the root
if len(parts) <= 1:
@@ -523,8 +527,8 @@ class SearchRepository:
async with db.scoped_session(self.session_maker) as session:
# Delete existing record if any
await session.execute(
text("DELETE FROM search_index WHERE permalink = :permalink"),
{"permalink": search_index_row.permalink},
text("DELETE FROM search_index WHERE permalink = :permalink AND project_id = :project_id"),
{"permalink": search_index_row.permalink, "project_id": self.project_id},
)
# Prepare data for insert with project_id
+40 -10
View File
@@ -22,6 +22,8 @@ from dateparser import parse
from pydantic import BaseModel, BeforeValidator, Field, model_validator
from basic_memory.config import ConfigManager
from basic_memory.file_utils import sanitize_for_filename
from basic_memory.utils import generate_permalink
@@ -53,22 +55,28 @@ def parse_timeframe(timeframe: str) -> datetime:
timeframe: Natural language timeframe like 'today', '1d', '1 week ago', etc.
Returns:
datetime: The parsed datetime for the start of the timeframe
datetime: The parsed datetime for the start of the timeframe, timezone-aware in local system timezone
Examples:
parse_timeframe('today') -> 2025-06-05 00:00:00 (start of today)
parse_timeframe('1d') -> 2025-06-04 14:50:00 (24 hours ago)
parse_timeframe('1 week ago') -> 2025-05-29 14:50:00 (1 week ago)
parse_timeframe('today') -> 2025-06-05 00:00:00-07:00 (start of today with local timezone)
parse_timeframe('1d') -> 2025-06-04 14:50:00-07:00 (24 hours ago with local timezone)
parse_timeframe('1 week ago') -> 2025-05-29 14:50:00-07:00 (1 week ago with local timezone)
"""
if timeframe.lower() == "today":
# Return start of today (00:00:00)
return datetime.combine(datetime.now().date(), time.min)
# Return start of today (00:00:00) in local timezone
naive_dt = datetime.combine(datetime.now().date(), time.min)
return naive_dt.astimezone()
else:
# Use dateparser for other formats
parsed = parse(timeframe)
if not parsed:
raise ValueError(f"Could not parse timeframe: {timeframe}")
return parsed
# If the parsed datetime is naive, make it timezone-aware in local system timezone
if parsed.tzinfo is None:
return parsed.astimezone()
else:
return parsed
def validate_timeframe(timeframe: str) -> str:
@@ -85,7 +93,7 @@ def validate_timeframe(timeframe: str) -> str:
parsed = parse_timeframe(timeframe)
# Convert to duration
now = datetime.now()
now = datetime.now().astimezone()
if parsed > now:
raise ValueError("Timeframe cannot be in the future")
@@ -184,13 +192,35 @@ class Entity(BaseModel):
default="text/markdown",
)
@property
def safe_title(self) -> str:
"""
A sanitized version of the title, which is safe for use on the filesystem. For example,
a title of "Coupon Enable/Disable Feature" should create a the file as "Coupon Enable-Disable Feature.md"
instead of creating a file named "Disable Feature.md" beneath the "Coupon Enable" directory.
Replaces POSIX and/or Windows style slashes as well as a few other characters that are not safe for filenames.
If kebab_filenames is True, then behavior is consistent with transformation used when generating permalink
strings (e.g. "Coupon Enable/Disable Feature" -> "coupon-enable-disable-feature").
"""
fixed_title = sanitize_for_filename(self.title)
app_config = ConfigManager().config
use_kebab_case = app_config.kebab_filenames
if use_kebab_case:
fixed_title = generate_permalink(file_path=fixed_title, split_extension=False)
return fixed_title
@property
def file_path(self):
"""Get the file path for this entity based on its permalink."""
safe_title = self.safe_title
if self.content_type == "text/markdown":
return f"{self.folder}/{self.title}.md" if self.folder else f"{self.title}.md"
return f"{self.folder}/{safe_title}.md" if self.folder else f"{safe_title}.md"
else:
return f"{self.folder}/{self.title}" if self.folder else self.title
return f"{self.folder}/{safe_title}" if self.folder else safe_title
@property
def permalink(self) -> Permalink:
+1
View File
@@ -32,3 +32,4 @@ class EntityImportResult(ImportResult):
entities: int = 0
relations: int = 0
skipped_entities: int = 0
+23 -11
View File
@@ -1,10 +1,10 @@
"""Schemas for memory context."""
from datetime import datetime
from typing import List, Optional, Annotated, Sequence
from typing import List, Optional, Annotated, Sequence, Literal, Union
from annotated_types import MinLen, MaxLen
from pydantic import BaseModel, Field, BeforeValidator, TypeAdapter
from pydantic import BaseModel, Field, BeforeValidator, TypeAdapter, ConfigDict
from basic_memory.schemas.search import SearchItemType
@@ -117,8 +117,10 @@ def memory_url_path(url: memory_url) -> str: # pyright: ignore
class EntitySummary(BaseModel):
"""Simplified entity representation."""
model_config = ConfigDict(json_encoders={datetime: lambda dt: dt.isoformat()})
type: str = "entity"
type: Literal["entity"] = "entity"
permalink: Optional[str]
title: str
content: Optional[str] = None
@@ -128,8 +130,10 @@ class EntitySummary(BaseModel):
class RelationSummary(BaseModel):
"""Simplified relation representation."""
model_config = ConfigDict(json_encoders={datetime: lambda dt: dt.isoformat()})
type: str = "relation"
type: Literal["relation"] = "relation"
title: str
file_path: str
permalink: str
@@ -141,8 +145,10 @@ class RelationSummary(BaseModel):
class ObservationSummary(BaseModel):
"""Simplified observation representation."""
model_config = ConfigDict(json_encoders={datetime: lambda dt: dt.isoformat()})
type: str = "observation"
type: Literal["observation"] = "observation"
title: str
file_path: str
permalink: str
@@ -153,6 +159,8 @@ class ObservationSummary(BaseModel):
class MemoryMetadata(BaseModel):
"""Simplified response metadata."""
model_config = ConfigDict(json_encoders={datetime: lambda dt: dt.isoformat()})
uri: Optional[str] = None
types: Optional[List[SearchItemType]] = None
@@ -169,17 +177,21 @@ class MemoryMetadata(BaseModel):
class ContextResult(BaseModel):
"""Context result containing a primary item with its observations and related items."""
primary_result: EntitySummary | RelationSummary | ObservationSummary = Field(
description="Primary item"
)
primary_result: Annotated[
Union[EntitySummary, RelationSummary, ObservationSummary],
Field(discriminator="type", description="Primary item")
]
observations: Sequence[ObservationSummary] = Field(
description="Observations belonging to this entity", default_factory=list
)
related_results: Sequence[EntitySummary | RelationSummary | ObservationSummary] = Field(
description="Related items", default_factory=list
)
related_results: Sequence[
Annotated[
Union[EntitySummary, RelationSummary, ObservationSummary],
Field(discriminator="type")
]
] = Field(description="Related items", default_factory=list)
class GraphContext(BaseModel):
+12 -2
View File
@@ -245,8 +245,8 @@ class ContextService:
# For compatibility with the old query, we still need this for filtering
values = ", ".join([f"('{t}', {i})" for t, i in type_id_pairs])
# Parameters for bindings
params = {"max_depth": max_depth, "max_results": max_results}
# Parameters for bindings - include project_id for security filtering
params = {"max_depth": max_depth, "max_results": max_results, "project_id": self.search_repository.project_id}
# Build date and timeframe filters conditionally based on since parameter
if since:
@@ -258,6 +258,10 @@ class ContextService:
date_filter = ""
relation_date_filter = ""
timeframe_condition = ""
# Add project filtering for security - ensure all entities and relations belong to the same project
project_filter = "AND e.project_id = :project_id"
relation_project_filter = "AND e_from.project_id = :project_id"
# Use a CTE that operates directly on entity and relation tables
# This avoids the overhead of the search_index virtual table
@@ -284,6 +288,7 @@ class ContextService:
FROM entity e
WHERE e.id IN ({entity_id_values})
{date_filter}
{project_filter}
UNION ALL
@@ -314,8 +319,12 @@ class ContextService:
JOIN entity e_from ON (
r.from_id = e_from.id
{relation_date_filter}
{relation_project_filter}
)
LEFT JOIN entity e_to ON (r.to_id = e_to.id)
WHERE eg.depth < :max_depth
-- Ensure to_entity (if exists) also belongs to same project
AND (r.to_id IS NULL OR e_to.project_id = :project_id)
UNION ALL
@@ -348,6 +357,7 @@ class ContextService:
ELSE eg.from_id
END
{date_filter}
{project_filter}
)
WHERE eg.depth < :max_depth
-- Only include entities connected by relations within timeframe if specified
@@ -106,8 +106,15 @@ class DirectoryService:
List of DirectoryNode objects matching the criteria
"""
# Normalize directory path
# Strip ./ prefix if present (handles relative path notation)
if dir_name.startswith("./"):
dir_name = dir_name[2:] # Remove "./" prefix
# Ensure path starts with "/"
if not dir_name.startswith("/"):
dir_name = f"/{dir_name}"
# Remove trailing slashes except for root
if dir_name != "/" and dir_name.endswith("/"):
dir_name = dir_name.rstrip("/")
+56 -10
View File
@@ -9,12 +9,13 @@ from loguru import logger
from sqlalchemy.exc import IntegrityError
from basic_memory.config import ProjectConfig, BasicMemoryConfig
from basic_memory.file_utils import has_frontmatter, parse_frontmatter, remove_frontmatter
from basic_memory.file_utils import has_frontmatter, parse_frontmatter, remove_frontmatter, dump_frontmatter
from basic_memory.markdown import EntityMarkdown
from basic_memory.markdown.entity_parser import EntityParser
from basic_memory.markdown.utils import entity_model_from_markdown, schema_to_markdown
from basic_memory.models import Entity as EntityModel
from basic_memory.models import Observation, Relation
from basic_memory.models.knowledge import Entity
from basic_memory.repository import ObservationRepository, RelationRepository
from basic_memory.repository.entity_repository import EntityRepository
from basic_memory.schemas import Entity as EntitySchema
@@ -44,6 +45,39 @@ class EntityService(BaseService[EntityModel]):
self.file_service = file_service
self.link_resolver = link_resolver
async def detect_file_path_conflicts(self, file_path: str) -> List[Entity]:
"""Detect potential file path conflicts for a given file path.
This checks for entities with similar file paths that might cause conflicts:
- Case sensitivity differences (Finance/file.md vs finance/file.md)
- Character encoding differences
- Hyphen vs space differences
- Unicode normalization differences
Args:
file_path: The file path to check for conflicts
Returns:
List of entities that might conflict with the given file path
"""
from basic_memory.utils import detect_potential_file_conflicts
conflicts = []
# Get all existing file paths
all_entities = await self.repository.find_all()
existing_paths = [entity.file_path for entity in all_entities]
# Use the enhanced conflict detection utility
conflicting_paths = detect_potential_file_conflicts(file_path, existing_paths)
# Find the entities corresponding to conflicting paths
for entity in all_entities:
if entity.file_path in conflicting_paths:
conflicts.append(entity)
return conflicts
async def resolve_permalink(
self, file_path: Permalink | Path, markdown: Optional[EntityMarkdown] = None
) -> str:
@@ -54,18 +88,30 @@ class EntityService(BaseService[EntityModel]):
2. If markdown has permalink but it's used by another file -> make unique
3. For existing files, keep current permalink from db
4. Generate new unique permalink from file path
Enhanced to detect and handle character-related conflicts.
"""
file_path_str = Path(file_path).as_posix()
# Check for potential file path conflicts before resolving permalink
conflicts = await self.detect_file_path_conflicts(file_path_str)
if conflicts:
logger.warning(
f"Detected potential file path conflicts for '{file_path_str}': "
f"{[entity.file_path for entity in conflicts]}"
)
# If markdown has explicit permalink, try to validate it
if markdown and markdown.frontmatter.permalink:
desired_permalink = markdown.frontmatter.permalink
existing = await self.repository.get_by_permalink(desired_permalink)
# If no conflict or it's our own file, use as is
if not existing or existing.file_path == str(file_path):
if not existing or existing.file_path == file_path_str:
return desired_permalink
# For existing files, try to find current permalink
existing = await self.repository.get_by_file_path(str(file_path))
existing = await self.repository.get_by_file_path(file_path_str)
if existing:
return existing.permalink
@@ -73,9 +119,9 @@ class EntityService(BaseService[EntityModel]):
if markdown and markdown.frontmatter.permalink:
desired_permalink = markdown.frontmatter.permalink
else:
desired_permalink = generate_permalink(file_path)
desired_permalink = generate_permalink(file_path_str)
# Make unique if needed
# Make unique if needed - enhanced to handle character conflicts
permalink = desired_permalink
suffix = 1
while await self.repository.get_by_permalink(permalink):
@@ -150,7 +196,7 @@ class EntityService(BaseService[EntityModel]):
post = await schema_to_markdown(schema)
# write file
final_content = frontmatter.dumps(post, sort_keys=False)
final_content = dump_frontmatter(post)
checksum = await self.file_service.write_file(file_path, final_content)
# parse entity from file
@@ -227,7 +273,7 @@ class EntityService(BaseService[EntityModel]):
merged_post = frontmatter.Post(post.content, **existing_markdown.frontmatter.metadata)
# write file
final_content = frontmatter.dumps(merged_post, sort_keys=False)
final_content = dump_frontmatter(merged_post)
checksum = await self.file_service.write_file(file_path, final_content)
# parse entity from file
@@ -237,7 +283,7 @@ class EntityService(BaseService[EntityModel]):
entity = await self.update_entity_and_observations(file_path, entity_markdown)
# add relations
await self.update_entity_relations(str(file_path), entity_markdown)
await self.update_entity_relations(file_path.as_posix(), entity_markdown)
# Set final checksum to match file
entity = await self.repository.update(entity.id, {"checksum": checksum})
@@ -328,7 +374,7 @@ class EntityService(BaseService[EntityModel]):
"""
logger.debug(f"Updating entity and observations: {file_path}")
db_entity = await self.repository.get_by_file_path(str(file_path))
db_entity = await self.repository.get_by_file_path(file_path.as_posix())
# Clear observations for entity
await self.observation_repository.delete_by_fields(entity_id=db_entity.id)
@@ -452,7 +498,7 @@ class EntityService(BaseService[EntityModel]):
# Update entity and its relationships
entity = await self.update_entity_and_observations(file_path, entity_markdown)
await self.update_entity_relations(str(file_path), entity_markdown)
await self.update_entity_relations(file_path.as_posix(), entity_markdown)
# Set final checksum to match file
entity = await self.repository.update(entity.id, {"checksum": checksum})
@@ -5,14 +5,12 @@ to ensure consistent application startup across all entry points.
"""
import asyncio
import shutil
from pathlib import Path
from loguru import logger
from basic_memory import db
from basic_memory.config import BasicMemoryConfig
from basic_memory.models import Project
from basic_memory.repository import ProjectRepository
@@ -70,63 +68,6 @@ async def reconcile_projects_with_config(app_config: BasicMemoryConfig):
logger.info("Continuing with initialization despite synchronization error")
async def migrate_legacy_projects(app_config: BasicMemoryConfig):
# Get database session - migrations handled centrally
_, session_maker = await db.get_or_create_db(
db_path=app_config.database_path,
db_type=db.DatabaseType.FILESYSTEM,
ensure_migrations=False,
)
logger.info("Migrating legacy projects...")
project_repository = ProjectRepository(session_maker)
# For each project in config.json, check if it has a .basic-memory dir
for project_name, project_path in app_config.projects.items():
legacy_dir = Path(project_path) / ".basic-memory"
if not legacy_dir.exists():
continue
logger.info(f"Detected legacy project directory: {legacy_dir}")
project = await project_repository.get_by_name(project_name)
if not project: # pragma: no cover
logger.error(f"Project {project_name} not found in database, skipping migration")
continue
logger.info(f"Starting migration for project: {project_name} (id: {project.id})")
await migrate_legacy_project_data(project, legacy_dir)
logger.info(f"Completed migration for project: {project_name}")
logger.info("Legacy projects successfully migrated")
async def migrate_legacy_project_data(project: Project, legacy_dir: Path) -> bool:
"""Check if project has legacy .basic-memory dir and migrate if needed.
Args:
project: The project to check and potentially migrate
Returns:
True if migration occurred, False otherwise
"""
# avoid circular imports
from basic_memory.cli.commands.sync import get_sync_service
sync_service = await get_sync_service(project)
sync_dir = Path(project.path)
logger.info(f"Sync starting project: {project.name}")
await sync_service.sync(sync_dir, project_name=project.name)
logger.info(f"Sync completed successfully for project: {project.name}")
# After successful sync, remove the legacy directory
try:
logger.info(f"Removing legacy directory: {legacy_dir}")
shutil.rmtree(legacy_dir)
return True
except Exception as e:
logger.error(f"Error removing legacy directory: {e}")
return False
async def initialize_file_sync(
app_config: BasicMemoryConfig,
):
@@ -186,16 +127,6 @@ async def initialize_file_sync(
sync_status_tracker.fail_project_sync(project.name, str(e))
# Continue with other projects even if one fails
# Mark migration complete if it was in progress
try:
from basic_memory.services.migration_service import migration_manager
if not migration_manager.is_ready: # pragma: no cover
migration_manager.mark_completed("Migration completed with file sync")
logger.info("Marked migration as completed after file sync")
except Exception as e: # pragma: no cover
logger.warning(f"Could not update migration status: {e}")
# Then start the watch service in the background
logger.info("Starting watch service for all projects")
# run the watch service
@@ -229,13 +160,7 @@ async def initialize_app(
# Reconcile projects from config.json with projects table
await reconcile_projects_with_config(app_config)
# Start background migration for legacy project data (non-blocking)
from basic_memory.services.migration_service import migration_manager
await migration_manager.start_background_migration(app_config)
logger.info("App initialization completed (migration running in background if needed)")
return migration_manager
def ensure_initialization(app_config: BasicMemoryConfig) -> None:
@@ -1,168 +0,0 @@
"""Migration service for handling background migrations and status tracking."""
import asyncio
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import Optional
from loguru import logger
from basic_memory.config import BasicMemoryConfig
class MigrationStatus(Enum):
"""Status of migration operations."""
NOT_NEEDED = "not_needed"
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
FAILED = "failed"
@dataclass
class MigrationState:
"""Current state of migration operations."""
status: MigrationStatus
message: str
progress: Optional[str] = None
error: Optional[str] = None
projects_migrated: int = 0
projects_total: int = 0
class MigrationManager:
"""Manages background migration operations and status tracking."""
def __init__(self):
self._state = MigrationState(
status=MigrationStatus.NOT_NEEDED, message="No migration required"
)
self._migration_task: Optional[asyncio.Task] = None
@property
def state(self) -> MigrationState:
"""Get current migration state."""
return self._state
@property
def is_ready(self) -> bool:
"""Check if the system is ready for normal operations."""
return self._state.status in (MigrationStatus.NOT_NEEDED, MigrationStatus.COMPLETED)
@property
def status_message(self) -> str:
"""Get a user-friendly status message."""
if self._state.status == MigrationStatus.IN_PROGRESS:
progress = (
f" ({self._state.projects_migrated}/{self._state.projects_total})"
if self._state.projects_total > 0
else ""
)
return f"🔄 File sync in progress{progress}: {self._state.message}. Use sync_status() tool for details."
elif self._state.status == MigrationStatus.FAILED:
return f"❌ File sync failed: {self._state.error or 'Unknown error'}. Use sync_status() tool for details."
elif self._state.status == MigrationStatus.COMPLETED:
return "✅ File sync completed successfully"
else:
return "✅ System ready"
async def check_migration_needed(self, app_config: BasicMemoryConfig) -> bool:
"""Check if migration is needed without performing it."""
from basic_memory import db
from basic_memory.repository import ProjectRepository
try:
# Get database session
_, session_maker = await db.get_or_create_db(
db_path=app_config.database_path, db_type=db.DatabaseType.FILESYSTEM
)
project_repository = ProjectRepository(session_maker)
# Check for legacy projects
legacy_projects = []
for project_name, project_path in app_config.projects.items():
legacy_dir = Path(project_path) / ".basic-memory"
if legacy_dir.exists():
project = await project_repository.get_by_name(project_name)
if project:
legacy_projects.append(project)
if legacy_projects:
self._state = MigrationState(
status=MigrationStatus.PENDING,
message="Legacy projects detected",
projects_total=len(legacy_projects),
)
return True
else:
self._state = MigrationState(
status=MigrationStatus.NOT_NEEDED, message="No migration required"
)
return False
except Exception as e:
logger.error(f"Error checking migration status: {e}")
self._state = MigrationState(
status=MigrationStatus.FAILED, message="Migration check failed", error=str(e)
)
return False
async def start_background_migration(self, app_config: BasicMemoryConfig) -> None:
"""Start migration in background if needed."""
if not await self.check_migration_needed(app_config):
return
if self._migration_task and not self._migration_task.done():
logger.info("Migration already in progress")
return
logger.info("Starting background migration")
self._migration_task = asyncio.create_task(self._run_migration(app_config))
async def _run_migration(self, app_config: BasicMemoryConfig) -> None:
"""Run the actual migration process."""
try:
self._state.status = MigrationStatus.IN_PROGRESS
self._state.message = "Migrating legacy projects"
# Import here to avoid circular imports
from basic_memory.services.initialization import migrate_legacy_projects
# Run the migration
await migrate_legacy_projects(app_config)
self._state = MigrationState(
status=MigrationStatus.COMPLETED, message="Migration completed successfully"
)
logger.info("Background migration completed successfully")
except Exception as e:
logger.error(f"Background migration failed: {e}")
self._state = MigrationState(
status=MigrationStatus.FAILED, message="Migration failed", error=str(e)
)
async def wait_for_completion(self, timeout: Optional[float] = None) -> bool:
"""Wait for migration to complete."""
if self.is_ready:
return True
if not self._migration_task:
return False
try:
await asyncio.wait_for(self._migration_task, timeout=timeout)
return self.is_ready
except asyncio.TimeoutError:
return False
def mark_completed(self, message: str = "Migration completed") -> None:
"""Mark migration as completed externally."""
self._state = MigrationState(status=MigrationStatus.COMPLETED, message=message)
# Global migration manager instance
migration_manager = MigrationManager()
+93 -36
View File
@@ -9,7 +9,6 @@ from typing import Dict, Optional, Sequence
from loguru import logger
from sqlalchemy import text
from basic_memory.config import config, app_config
from basic_memory.models import Project
from basic_memory.repository.project_repository import ProjectRepository
from basic_memory.schemas import (
@@ -18,9 +17,8 @@ from basic_memory.schemas import (
ProjectStatistics,
SystemStatus,
)
from basic_memory.config import WATCH_STATUS_JSON
from basic_memory.config import WATCH_STATUS_JSON, ConfigManager, get_project_config, ProjectConfig
from basic_memory.utils import generate_permalink
from basic_memory.config import config_manager
class ProjectService:
@@ -33,6 +31,24 @@ class ProjectService:
super().__init__()
self.repository = repository
@property
def config_manager(self) -> ConfigManager:
"""Get a ConfigManager instance.
Returns:
Fresh ConfigManager instance for each access
"""
return ConfigManager()
@property
def config(self) -> ProjectConfig:
"""Get the current project configuration.
Returns:
Current project configuration
"""
return get_project_config()
@property
def projects(self) -> Dict[str, str]:
"""Get all configured projects.
@@ -40,7 +56,7 @@ class ProjectService:
Returns:
Dict mapping project names to their file paths
"""
return config_manager.projects
return self.config_manager.projects
@property
def default_project(self) -> str:
@@ -49,7 +65,7 @@ class ProjectService:
Returns:
The name of the default project
"""
return config_manager.default_project
return self.config_manager.default_project
@property
def current_project(self) -> str:
@@ -58,7 +74,7 @@ class ProjectService:
Returns:
The name of the current project
"""
return os.environ.get("BASIC_MEMORY_PROJECT", config_manager.default_project)
return os.environ.get("BASIC_MEMORY_PROJECT", self.config_manager.default_project)
async def list_projects(self) -> Sequence[Project]:
return await self.repository.find_all()
@@ -84,10 +100,10 @@ class ProjectService:
raise ValueError("Repository is required for add_project")
# Resolve to absolute path
resolved_path = os.path.abspath(os.path.expanduser(path))
resolved_path = Path(os.path.abspath(os.path.expanduser(path))).as_posix()
# First add to config file (this will validate the project doesn't exist)
project_config = config_manager.add_project(name, resolved_path)
project_config = self.config_manager.add_project(name, resolved_path)
# Then add to database
project_data = {
@@ -103,7 +119,7 @@ class ProjectService:
# If this should be the default project, ensure only one default exists
if set_default:
await self.repository.set_as_default(created_project.id)
config_manager.set_default_project(name)
self.config_manager.set_default_project(name)
logger.info(f"Project '{name}' set as default")
logger.info(f"Project '{name}' added at {resolved_path}")
@@ -121,10 +137,10 @@ class ProjectService:
raise ValueError("Repository is required for remove_project")
# First remove from config (this will validate the project exists and is not default)
config_manager.remove_project(name)
self.config_manager.remove_project(name)
# Then remove from database
project = await self.repository.get_by_name(name)
# Then remove from database using robust lookup
project = await self.get_project(name)
if project:
await self.repository.delete(project.id)
@@ -143,10 +159,10 @@ class ProjectService:
raise ValueError("Repository is required for set_default_project")
# First update config file (this will validate the project exists)
config_manager.set_default_project(name)
self.config_manager.set_default_project(name)
# Then update database
project = await self.repository.get_by_name(name)
# Then update database using the same lookup logic as get_project
project = await self.get_project(name)
if project:
await self.repository.set_as_default(project.id)
else:
@@ -196,7 +212,7 @@ class ProjectService:
elif len(default_projects) == 0: # pragma: no cover
# No default project - set the config default as default
# This is defensive code for edge cases where no default exists
config_default = config_manager.default_project # pragma: no cover
config_default = self.config_manager.default_project # pragma: no cover
config_project = await self.repository.get_by_name(config_default) # pragma: no cover
if config_project: # pragma: no cover
await self.repository.set_as_default(config_project.id) # pragma: no cover
@@ -221,7 +237,7 @@ class ProjectService:
db_projects_by_permalink = {p.permalink: p for p in db_projects}
# Get all projects from configuration and normalize names if needed
config_projects = config_manager.projects.copy()
config_projects = self.config_manager.projects.copy()
updated_config = {}
config_updated = False
@@ -237,8 +253,9 @@ class ProjectService:
# Update the configuration if any changes were made
if config_updated:
config_manager.config.projects = updated_config
config_manager.save_config(config_manager.config)
config = self.config_manager.load_config()
config.projects = updated_config
self.config_manager.save_config(config)
logger.info("Config updated with normalized project names")
# Use the normalized config for further processing
@@ -261,19 +278,19 @@ class ProjectService:
for name, project in db_projects_by_permalink.items():
if name not in config_projects:
logger.info(f"Adding project '{name}' to configuration")
config_manager.add_project(name, project.path)
self.config_manager.add_project(name, project.path)
# Ensure database default project state is consistent
await self._ensure_single_default_project()
# Make sure default project is synchronized between config and database
db_default = await self.repository.get_default_project()
config_default = config_manager.default_project
config_default = self.config_manager.default_project
if db_default and db_default.name != config_default:
# Update config to match DB default
logger.info(f"Updating default project in config to '{db_default.name}'")
config_manager.set_default_project(db_default.name)
self.config_manager.set_default_project(db_default.name)
elif not db_default and config_default:
# Update DB to match config default (if the project exists)
project = await self.repository.get_by_name(config_default)
@@ -292,6 +309,47 @@ class ProjectService:
# MCP components might not be available in all contexts
logger.debug("MCP session not available, skipping session refresh")
async def move_project(self, name: str, new_path: str) -> None:
"""Move a project to a new location.
Args:
name: The name of the project to move
new_path: The new absolute path for the project
Raises:
ValueError: If the project doesn't exist or repository isn't initialized
"""
if not self.repository:
raise ValueError("Repository is required for move_project")
# Resolve to absolute path
resolved_path = Path(os.path.abspath(os.path.expanduser(new_path))).as_posix()
# Validate project exists in config
if name not in self.config_manager.projects:
raise ValueError(f"Project '{name}' not found in configuration")
# Create the new directory if it doesn't exist
Path(resolved_path).mkdir(parents=True, exist_ok=True)
# Update in configuration
config = self.config_manager.load_config()
old_path = config.projects[name]
config.projects[name] = resolved_path
self.config_manager.save_config(config)
# Update in database using robust lookup
project = await self.get_project(name)
if project:
await self.repository.update_path(project.id, resolved_path)
logger.info(f"Moved project '{name}' from {old_path} to {resolved_path}")
else:
logger.error(f"Project '{name}' exists in config but not in database")
# Restore the old path in config since DB update failed
config.projects[name] = old_path
self.config_manager.save_config(config)
raise ValueError(f"Project '{name}' not found in database")
async def update_project( # pragma: no cover
self, name: str, updated_path: Optional[str] = None, is_active: Optional[bool] = None
) -> None:
@@ -309,24 +367,23 @@ class ProjectService:
raise ValueError("Repository is required for update_project")
# Validate project exists in config
if name not in config_manager.projects:
if name not in self.config_manager.projects:
raise ValueError(f"Project '{name}' not found in configuration")
# Get project from database
project = await self.repository.get_by_name(name)
# Get project from database using robust lookup
project = await self.get_project(name)
if not project:
logger.error(f"Project '{name}' exists in config but not in database")
return
# Update path if provided
if updated_path:
resolved_path = os.path.abspath(os.path.expanduser(updated_path))
resolved_path = Path(os.path.abspath(os.path.expanduser(updated_path))).as_posix()
# Update in config
projects = config_manager.config.projects.copy()
projects[name] = resolved_path
config_manager.config.projects = projects
config_manager.save_config(config_manager.config)
config = self.config_manager.load_config()
config.projects[name] = resolved_path
self.config_manager.save_config(config)
# Update in database
project.path = resolved_path
@@ -347,7 +404,7 @@ class ProjectService:
if active_projects:
new_default = active_projects[0]
await self.repository.set_as_default(new_default.id)
config_manager.set_default_project(new_default.name)
self.config_manager.set_default_project(new_default.name)
logger.info(
f"Changed default project to '{new_default.name}' as '{name}' was deactivated"
)
@@ -365,9 +422,9 @@ class ProjectService:
raise ValueError("Repository is required for get_project_info")
# Use specified project or fall back to config project
project_name = project_name or config.project
project_name = project_name or self.config.project
# Get project path from configuration
name, project_path = config_manager.get_project(project_name)
name, project_path = self.config_manager.get_project(project_name)
if not name: # pragma: no cover
raise ValueError(f"Project '{project_name}' not found in configuration")
@@ -393,11 +450,11 @@ class ProjectService:
db_projects_by_permalink = {p.permalink: p for p in db_projects}
# Get default project info
default_project = config_manager.default_project
default_project = self.config_manager.default_project
# Convert config projects to include database info
enhanced_projects = {}
for name, path in config_manager.projects.items():
for name, path in self.config_manager.projects.items():
config_permalink = generate_permalink(name)
db_project = db_projects_by_permalink.get(config_permalink)
enhanced_projects[name] = {
@@ -673,7 +730,7 @@ class ProjectService:
import basic_memory
# Get database information
db_path = app_config.database_path
db_path = self.config_manager.config.database_path
db_size = db_path.stat().st_size if db_path.exists() else 0
db_size_readable = f"{db_size / (1024 * 1024):.2f} MB"
+4 -3
View File
@@ -2,7 +2,7 @@ import asyncio
from loguru import logger
from basic_memory.config import config as project_config
from basic_memory.config import get_project_config
from basic_memory.sync import SyncService, WatchService
@@ -11,9 +11,10 @@ async def sync_and_watch(
): # pragma: no cover
"""Run sync and watch service."""
logger.info(f"Starting watch service to sync file changes in dir: {project_config.home}")
config = get_project_config()
logger.info(f"Starting watch service to sync file changes in dir: {config.home}")
# full sync
await sync_service.sync(project_config.home)
await sync_service.sync(config.home)
# watch changes
await watch_service.run()
+53 -4
View File
@@ -357,8 +357,8 @@ class SyncService:
# get file timestamps
file_stats = self.file_service.file_stats(path)
created = datetime.fromtimestamp(file_stats.st_ctime)
modified = datetime.fromtimestamp(file_stats.st_mtime)
created = datetime.fromtimestamp(file_stats.st_ctime).astimezone()
modified = datetime.fromtimestamp(file_stats.st_mtime).astimezone()
# get mime type
content_type = self.file_service.content_type(path)
@@ -453,6 +453,36 @@ class SyncService:
entity = await self.entity_repository.get_by_file_path(old_path)
if entity:
# Check if destination path is already occupied by another entity
existing_at_destination = await self.entity_repository.get_by_file_path(new_path)
if existing_at_destination and existing_at_destination.id != entity.id:
# Handle the conflict - this could be a file swap or replacement scenario
logger.warning(
f"File path conflict detected during move: "
f"entity_id={entity.id} trying to move from '{old_path}' to '{new_path}', "
f"but entity_id={existing_at_destination.id} already occupies '{new_path}'"
)
# Check if this is a file swap (the destination entity is being moved to our old path)
# This would indicate a simultaneous move operation
old_path_after_swap = await self.entity_repository.get_by_file_path(old_path)
if old_path_after_swap and old_path_after_swap.id == existing_at_destination.id:
logger.info(f"Detected file swap between '{old_path}' and '{new_path}'")
# This is a swap scenario - both moves should succeed
# We'll allow this to proceed since the other file has moved out
else:
# This is a conflict where the destination is occupied
raise ValueError(
f"Cannot move entity from '{old_path}' to '{new_path}': "
f"destination path is already occupied by another file. "
f"This may be caused by: "
f"1. Conflicting file names with different character encodings, "
f"2. Case sensitivity differences (e.g., 'Finance/' vs 'finance/'), "
f"3. Character conflicts between hyphens in filenames and generated permalinks, "
f"4. Files with similar names containing special characters. "
f"Try renaming one of the conflicting files to resolve this issue."
)
# Update file_path in all cases
updates = {"file_path": new_path}
@@ -477,7 +507,26 @@ class SyncService:
f"new_checksum={new_checksum}"
)
updated = await self.entity_repository.update(entity.id, updates)
try:
updated = await self.entity_repository.update(entity.id, updates)
except Exception as e:
# Catch any database integrity errors and provide helpful context
if "UNIQUE constraint failed" in str(e):
logger.error(
f"Database constraint violation during move: "
f"entity_id={entity.id}, old_path='{old_path}', new_path='{new_path}'"
)
raise ValueError(
f"Cannot complete move from '{old_path}' to '{new_path}': "
f"a database constraint was violated. This usually indicates "
f"a file path or permalink conflict. Please check for: "
f"1. Duplicate file names, "
f"2. Case sensitivity issues (e.g., 'File.md' vs 'file.md'), "
f"3. Character encoding conflicts in file names."
) from e
else:
# Re-raise other exceptions as-is
raise
if updated is None: # pragma: no cover
logger.error(
@@ -570,7 +619,7 @@ class SyncService:
continue
path = Path(root) / filename
rel_path = str(path.relative_to(directory))
rel_path = path.relative_to(directory).as_posix()
checksum = await self.file_service.compute_checksum(rel_path)
result.files[rel_path] = checksum
result.checksums[checksum] = rel_path
+31 -8
View File
@@ -197,7 +197,7 @@ class WatchService:
for change, path in changes:
# convert to relative path
relative_path = str(Path(path).relative_to(directory))
relative_path = Path(path).relative_to(directory).as_posix()
# Skip .tmp files - they're temporary and shouldn't be synced
if relative_path.endswith(".tmp"):
@@ -284,13 +284,36 @@ class WatchService:
# Process deletes
for path in deletes:
if path not in processed:
logger.debug("Processing deleted file", path=path)
await sync_service.handle_delete(path)
self.state.add_event(path=path, action="deleted", status="success")
self.console.print(f"[red]✕[/red] {path}")
logger.info(f"deleted: {path}")
processed.add(path)
delete_count += 1
# Check if file still exists on disk (vim atomic write edge case)
full_path = directory / path
if full_path.exists() and full_path.is_file():
# File still exists despite DELETE event - treat as modification
logger.debug("File exists despite DELETE event, treating as modification", path=path)
entity, checksum = await sync_service.sync_file(path, new=False)
self.state.add_event(path=path, action="modified", status="success", checksum=checksum)
self.console.print(f"[yellow]✎[/yellow] {path} (atomic write)")
logger.info(f"atomic write detected: {path}")
processed.add(path)
modify_count += 1
else:
# Check if this was a directory - skip if so
# (we can't tell if the deleted path was a directory since it no longer exists,
# so we check if there's an entity in the database for it)
entity = await sync_service.entity_repository.get_by_file_path(path)
if entity is None:
# No entity means this was likely a directory - skip it
logger.debug(f"Skipping deleted path with no entity (likely directory), path={path}")
processed.add(path)
continue
# File truly deleted
logger.debug("Processing deleted file", path=path)
await sync_service.handle_delete(path)
self.state.add_event(path=path, action="deleted", status="success")
self.console.print(f"[red]✕[/red] {path}")
logger.info(f"deleted: {path}")
processed.add(path)
delete_count += 1
# Process adds
for path in adds:
+239 -76
View File
@@ -5,11 +5,12 @@ import os
import logging
import re
import sys
import unicodedata
from datetime import datetime
from pathlib import Path
from typing import Optional, Protocol, Union, runtime_checkable, List, Any
from typing import Optional, Protocol, Union, runtime_checkable, List
from loguru import logger
from unidecode import unidecode
@runtime_checkable
@@ -27,9 +28,11 @@ FilePath = Union[Path, str]
logging.getLogger("opentelemetry.sdk.metrics._internal.instrument").setLevel(logging.ERROR)
def generate_permalink(file_path: Union[Path, str, Any]) -> str:
"""
Generate a permalink from a file path.
def generate_permalink(file_path: Union[Path, str, PathLike], split_extension: bool = True) -> str:
"""Generate a stable permalink from a file path.
Args:
file_path: Original file path (str, Path, or PathLike)
Returns:
Normalized permalink that matches validation rules. Converts spaces and underscores
@@ -38,7 +41,7 @@ def generate_permalink(file_path: Union[Path, str, Any]) -> str:
Examples:
>>> generate_permalink("docs/My Feature.md")
'docs/my-feature'
>>> generate_permalink("specs/API_v2.md")
>>> generate_permalink("specs/API (v2).md")
'specs/api-v2'
>>> generate_permalink("design/unified_model_refactor.md")
'design/unified-model-refactor'
@@ -46,84 +49,99 @@ def generate_permalink(file_path: Union[Path, str, Any]) -> str:
'中文/测试文档'
"""
# Convert Path to string if needed
path_str = str(file_path)
path_str = Path(str(file_path)).as_posix()
# Remove extension
base = os.path.splitext(path_str)[0]
# Remove extension (for now, possibly)
(base, extension) = os.path.splitext(path_str)
# Create a transliteration mapping for specific characters
transliteration_map = {
"ø": "o", # Handle Søren -> soren
"å": "a", # Handle Kierkegård -> kierkegard
"ü": "u", # Handle Müller -> muller
"é": "e", # Handle Café -> cafe
"è": "e", # Handle Mère -> mere
"ê": "e", # Handle Fête -> fete
"à": "a", # Handle À la mode -> a la mode
"ç": "c", # Handle Façade -> facade
"ñ": "n", # Handle Niño -> nino
"ö": "o", # Handle Björk -> bjork
"ä": "a", # Handle Häagen -> haagen
# Add more mappings as needed
}
# Process character by character, transliterating Latin characters with diacritics
result = ""
for char in base:
# Direct mapping for known characters
if char.lower() in transliteration_map:
result += transliteration_map[char.lower()]
# General case using Unicode normalization
elif unicodedata.category(char).startswith("L") and ord(char) > 127:
# Decompose the character (e.g., ü -> u + combining diaeresis)
decomposed = unicodedata.normalize("NFD", char)
# If decomposition produced multiple characters and first one is ASCII
if len(decomposed) > 1 and ord(decomposed[0]) < 128:
# Keep only the base character
result += decomposed[0].lower()
else:
# For non-Latin scripts like Chinese, preserve the character
result += char
else:
# Add the character as is
result += char
# Handle special punctuation cases for apostrophes
result = result.replace("'", "")
# Insert dash between camelCase
# This regex finds boundaries between lowercase and uppercase letters
result = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", result)
# Insert dash between Chinese and Latin character boundaries
# This is needed for cases like "中文English" -> "中文-english"
result = re.sub(r"([\u4e00-\u9fff])([a-zA-Z])", r"\1-\2", result)
result = re.sub(r"([a-zA-Z])([\u4e00-\u9fff])", r"\1-\2", result)
# Convert ASCII letters to lowercase, preserve non-ASCII characters
lower_text = "".join(c.lower() if c.isascii() and c.isalpha() else c for c in result)
# Replace underscores with hyphens
text_with_hyphens = lower_text.replace("_", "-")
# Replace spaces and unsafe ASCII characters with hyphens, but preserve non-ASCII characters
# Include common Chinese character ranges and other non-ASCII characters
clean_text = re.sub(
r"[^a-z0-9\u4e00-\u9fff\u3000-\u303f\u3400-\u4dbf/\-]", "-", text_with_hyphens
# Check if we have CJK characters that should be preserved
# CJK ranges: \u4e00-\u9fff (CJK Unified Ideographs), \u3000-\u303f (CJK symbols),
# \u3400-\u4dbf (CJK Extension A), \uff00-\uffef (Fullwidth forms)
has_cjk_chars = any(
"\u4e00" <= char <= "\u9fff"
or "\u3000" <= char <= "\u303f"
or "\u3400" <= char <= "\u4dbf"
or "\uff00" <= char <= "\uffef"
for char in base
)
if has_cjk_chars:
# For text with CJK characters, selectively transliterate only Latin accented chars
result = ""
for char in base:
if (
"\u4e00" <= char <= "\u9fff"
or "\u3000" <= char <= "\u303f"
or "\u3400" <= char <= "\u4dbf"
):
# Preserve CJK ideographs and symbols
result += char
elif "\uff00" <= char <= "\uffef":
# Remove Chinese fullwidth punctuation entirely (like ,!?)
continue
else:
# Transliterate Latin accented characters to ASCII
result += unidecode(char)
# Insert hyphens between CJK and Latin character transitions
# Match: CJK followed by Latin letter/digit, or Latin letter/digit followed by CJK
result = re.sub(
r"([\u4e00-\u9fff\u3000-\u303f\u3400-\u4dbf])([a-zA-Z0-9])", r"\1-\2", result
)
result = re.sub(
r"([a-zA-Z0-9])([\u4e00-\u9fff\u3000-\u303f\u3400-\u4dbf])", r"\1-\2", result
)
# Insert dash between camelCase
result = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", result)
# Convert ASCII letters to lowercase, preserve CJK
lower_text = "".join(c.lower() if c.isascii() and c.isalpha() else c for c in result)
# Replace underscores with hyphens
text_with_hyphens = lower_text.replace("_", "-")
# Remove apostrophes entirely (don't replace with hyphens)
text_no_apostrophes = text_with_hyphens.replace("'", "")
# Replace unsafe chars with hyphens, but preserve CJK characters
clean_text = re.sub(
r"[^a-z0-9\u4e00-\u9fff\u3000-\u303f\u3400-\u4dbf/\-]", "-", text_no_apostrophes
)
else:
# Original ASCII-only processing for backward compatibility
# Transliterate unicode to ascii
ascii_text = unidecode(base)
# Insert dash between camelCase
ascii_text = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", ascii_text)
# Convert to lowercase
lower_text = ascii_text.lower()
# replace underscores with hyphens
text_with_hyphens = lower_text.replace("_", "-")
# Remove apostrophes entirely (don't replace with hyphens)
text_no_apostrophes = text_with_hyphens.replace("'", "")
# Replace remaining invalid chars with hyphens
clean_text = re.sub(r"[^a-z0-9/\-]", "-", text_no_apostrophes)
# Collapse multiple hyphens
clean_text = re.sub(r"-+", "-", clean_text)
# Remove hyphens between adjacent Chinese characters only
# This handles cases like "你好-世界" -> "你好世界"
clean_text = re.sub(r"([\u4e00-\u9fff])-([\u4e00-\u9fff])", r"\1\2", clean_text)
# Clean each path segment
segments = clean_text.split("/")
clean_segments = [s.strip("-") for s in segments]
return "/".join(clean_segments)
return_val = "/".join(clean_segments)
# Append file extension back, if necessary
if not split_extension and extension:
return_val += extension
return return_val
def setup_logging(
@@ -173,8 +191,6 @@ def setup_logging(
"httpx": logging.WARNING,
# File watching logs
"watchfiles.main": logging.WARNING,
# SQLAlchemy deprecation warnings
"sqlalchemy": logging.WARNING,
}
# Set log levels for noisy loggers
@@ -203,8 +219,21 @@ def parse_tags(tags: Union[List[str], str, None]) -> List[str]:
# First strip whitespace, then strip leading '#' characters to prevent accumulation
return [tag.strip().lstrip("#") for tag in tags if tag and tag.strip()]
# Process comma-separated string of tags
# Process string input
if isinstance(tags, str):
# Check if it's a JSON array string (common issue from AI assistants)
import json
if tags.strip().startswith('[') and tags.strip().endswith(']'):
try:
# Try to parse as JSON array
parsed_json = json.loads(tags)
if isinstance(parsed_json, list):
# Recursively parse the JSON array as a list
return parse_tags(parsed_json)
except json.JSONDecodeError:
# Not valid JSON, fall through to comma-separated parsing
pass
# Split by comma, strip whitespace, then strip leading '#' characters
return [tag.strip().lstrip("#") for tag in tags.split(",") if tag and tag.strip()]
@@ -214,3 +243,137 @@ def parse_tags(tags: Union[List[str], str, None]) -> List[str]:
except (ValueError, TypeError): # pragma: no cover
logger.warning(f"Couldn't parse tags from input of type {type(tags)}: {tags}")
return []
def normalize_newlines(multiline: str) -> str:
"""Replace any \r\n, \r, or \n with the native newline.
Args:
multiline: String containing any mixture of newlines.
Returns:
A string with normalized newlines native to the platform.
"""
return re.sub(r"\r\n?|\n", os.linesep, multiline)
def normalize_file_path_for_comparison(file_path: str) -> str:
"""Normalize a file path for conflict detection.
This function normalizes file paths to help detect potential conflicts:
- Converts to lowercase for case-insensitive comparison
- Normalizes Unicode characters
- Handles path separators consistently
Args:
file_path: The file path to normalize
Returns:
Normalized file path for comparison purposes
"""
import unicodedata
# Convert to lowercase for case-insensitive comparison
normalized = file_path.lower()
# Normalize Unicode characters (NFD normalization)
normalized = unicodedata.normalize("NFD", normalized)
# Replace path separators with forward slashes
normalized = normalized.replace("\\", "/")
# Remove multiple slashes
normalized = re.sub(r"/+", "/", normalized)
return normalized
def detect_potential_file_conflicts(file_path: str, existing_paths: List[str]) -> List[str]:
"""Detect potential conflicts between a file path and existing paths.
This function checks for various types of conflicts:
- Case sensitivity differences
- Unicode normalization differences
- Path separator differences
- Permalink generation conflicts
Args:
file_path: The file path to check
existing_paths: List of existing file paths to check against
Returns:
List of existing paths that might conflict with the given file path
"""
conflicts = []
# Normalize the input file path
normalized_input = normalize_file_path_for_comparison(file_path)
input_permalink = generate_permalink(file_path)
for existing_path in existing_paths:
# Skip identical paths
if existing_path == file_path:
continue
# Check for case-insensitive path conflicts
normalized_existing = normalize_file_path_for_comparison(existing_path)
if normalized_input == normalized_existing:
conflicts.append(existing_path)
continue
# Check for permalink conflicts
existing_permalink = generate_permalink(existing_path)
if input_permalink == existing_permalink:
conflicts.append(existing_path)
continue
return conflicts
def validate_project_path(path: str, project_path: Path) -> bool:
"""Ensure path stays within project boundaries."""
# Allow empty strings as they resolve to the project root
if not path:
return True
# Check for obvious path traversal patterns first
if ".." in path or "~" in path:
return False
# Check for Windows-style path traversal (even on Unix systems)
if "\\.." in path or path.startswith("\\"):
return False
# Block absolute paths (Unix-style starting with / or Windows-style with drive letters)
if path.startswith("/") or (len(path) >= 2 and path[1] == ":"):
return False
# Block paths with control characters (but allow whitespace that will be stripped)
if path.strip() and any(ord(c) < 32 and c not in [" ", "\t"] for c in path):
return False
try:
resolved = (project_path / path).resolve()
return resolved.is_relative_to(project_path.resolve())
except (ValueError, OSError):
return False
def ensure_timezone_aware(dt: datetime) -> datetime:
"""Ensure a datetime is timezone-aware using system timezone.
If the datetime is naive, convert it to timezone-aware using the system's local timezone.
If it's already timezone-aware, return it unchanged.
Args:
dt: The datetime to ensure is timezone-aware
Returns:
A timezone-aware datetime
"""
if dt.tzinfo is None:
# Naive datetime - assume it's in local time and add timezone
return dt.astimezone()
else:
# Already timezone-aware
return dt
-158
View File
@@ -1,158 +0,0 @@
"""Integration test for database reset command.
This test validates the fix for GitHub issue #151 where the reset command
was only removing the SQLite database but leaving project configuration
intact in ~/.basic-memory/config.json.
The test verifies that the reset command now:
1. Removes the SQLite database
2. Resets project configuration to default state (main project only)
3. Recreates empty database
"""
import json
import pytest
import tempfile
from pathlib import Path
from unittest.mock import patch
@pytest.mark.asyncio
async def test_reset_config_file_behavior(config_manager):
"""Test that reset command properly updates the config.json file."""
# Step 1: Set up initial state with multiple projects in config
original_projects = {
"project1": "/path/to/project1",
"project2": "/path/to/project2",
"user-project": "/home/user/documents",
}
config_manager.config.projects = original_projects.copy()
config_manager.config.default_project = "user-project"
# Step 2: Save the config to a temporary file to simulate the real config file
with tempfile.TemporaryDirectory() as temp_dir:
temp_config_file = Path(temp_dir) / "config.json"
config_manager.config_file = temp_config_file
config_manager.save_config(config_manager.config)
# Step 3: Verify the config file contains the multiple projects
config_json = json.loads(temp_config_file.read_text())
assert len(config_json["projects"]) == 3
assert config_json["default_project"] == "user-project"
assert "project1" in config_json["projects"]
assert "project2" in config_json["projects"]
assert "user-project" in config_json["projects"]
# Step 4: Simulate the reset command's configuration reset behavior
# This is the exact fix for issue #151
with patch("pathlib.Path.home") as mock_home:
mock_home.return_value = Path("/home/testuser")
# Apply the reset logic from the reset command
config_manager.config.projects = {"main": str(Path.home() / "basic-memory")}
config_manager.config.default_project = "main"
config_manager.save_config(config_manager.config)
# Step 5: Read the config file and verify it was properly reset
updated_config_json = json.loads(temp_config_file.read_text())
# Should now only have the main project
assert len(updated_config_json["projects"]) == 1
assert "main" in updated_config_json["projects"]
assert updated_config_json["projects"]["main"] == "/home/testuser/basic-memory"
assert updated_config_json["default_project"] == "main"
# All original projects should be gone from the file
assert "project1" not in updated_config_json["projects"]
assert "project2" not in updated_config_json["projects"]
assert "user-project" not in updated_config_json["projects"]
# This validates that issue #151 is fixed:
# Before the fix, these projects would persist in config.json after reset
# After the fix, only the default "main" project remains
@pytest.mark.asyncio
async def test_reset_command_source_code_validation():
"""Validate that the reset command source contains the required fix."""
# This test ensures the fix for issue #151 is present in the source code
reset_source_path = (
Path(__file__).parent.parent.parent / "src" / "basic_memory" / "cli" / "commands" / "db.py"
)
reset_source = reset_source_path.read_text()
# Verify the key components of the fix are present
required_lines = [
"# Reset project configuration",
'config_manager.config.projects = {"main": str(Path.home() / "basic-memory")}',
'config_manager.config.default_project = "main"',
"config_manager.save_config(config_manager.config)",
'logger.info("Project configuration reset to default")',
]
for line in required_lines:
assert line in reset_source, f"Required fix line not found: {line}"
# Verify the fix is in the correct location (after database deletion, before recreation)
lines = reset_source.split("\n")
# Find key markers
db_deletion_line = None
config_reset_line = None
db_recreation_line = None
for i, line in enumerate(lines):
if "db_path.unlink()" in line:
db_deletion_line = i
elif "config_manager.config.projects = {" in line:
config_reset_line = i
elif "asyncio.run(db.run_migrations" in line:
db_recreation_line = i
# Verify the order is correct
assert db_deletion_line is not None, "Database deletion code not found"
assert config_reset_line is not None, "Config reset code not found"
assert db_recreation_line is not None, "Database recreation code not found"
# Config reset should be after db deletion and before db recreation
assert db_deletion_line < config_reset_line < db_recreation_line, (
"Config reset is not in the correct order in the reset command"
)
@pytest.mark.asyncio
async def test_config_reset_behavior_simulation(config_manager):
"""Test the specific configuration reset behavior that fixes issue #151."""
# Step 1: Set up the problem state (multiple projects in config)
original_projects = {
"project1": "/path/to/project1",
"project2": "/path/to/project2",
"user-project": "/home/user/documents",
}
config_manager.config.projects = original_projects.copy()
config_manager.config.default_project = "user-project"
# Verify the problem state
assert len(config_manager.config.projects) == 3
assert config_manager.config.default_project == "user-project"
# Step 2: Apply the reset fix (simulate what reset command does)
with patch("pathlib.Path.home") as mock_home:
mock_home.return_value = Path("/home/testuser")
# This is the exact code from the reset command that fixes issue #151
config_manager.config.projects = {"main": str(Path.home() / "basic-memory")}
config_manager.config.default_project = "main"
# Note: We don't call save_config in test to avoid file operations
# Step 3: Verify the fix worked
assert len(config_manager.config.projects) == 1
assert "main" in config_manager.config.projects
assert config_manager.config.projects["main"] == "/home/testuser/basic-memory"
assert config_manager.config.default_project == "main"
# Step 4: Verify original projects are gone
for project_name in original_projects:
assert project_name not in config_manager.config.projects
+27 -40
View File
@@ -58,16 +58,12 @@ from pathlib import Path
from httpx import AsyncClient, ASGITransport
import basic_memory.config
import basic_memory.mcp.project_session
from basic_memory.config import BasicMemoryConfig, ProjectConfig, ConfigManager
from basic_memory.db import engine_session_factory, DatabaseType
from basic_memory.models import Project
from basic_memory.repository.project_repository import ProjectRepository
from fastapi import FastAPI
from basic_memory.api.app import app as fastapi_app
from basic_memory.deps import get_project_config, get_engine_factory, get_app_config
@@ -93,12 +89,12 @@ async def engine_factory(tmp_path):
@pytest_asyncio.fixture(scope="function")
async def test_project(tmp_path, engine_factory) -> Project:
async def test_project(config_home, engine_factory) -> Project:
"""Create a test project."""
project_data = {
"name": "test-project",
"description": "Project used for integration tests",
"path": str(tmp_path),
"path": str(config_home),
"is_active": True,
"is_default": True,
}
@@ -112,55 +108,50 @@ async def test_project(tmp_path, engine_factory) -> Project:
@pytest.fixture
def config_home(tmp_path, monkeypatch) -> Path:
monkeypatch.setenv("HOME", str(tmp_path))
# Set BASIC_MEMORY_HOME to the test directory
monkeypatch.setenv("BASIC_MEMORY_HOME", str(tmp_path / "basic-memory"))
return tmp_path
@pytest.fixture(scope="function")
def app_config(config_home, test_project, tmp_path, monkeypatch) -> BasicMemoryConfig:
@pytest.fixture(scope="function", autouse=True)
def app_config(config_home, tmp_path, monkeypatch) -> BasicMemoryConfig:
"""Create test app configuration."""
projects = {test_project.name: str(test_project.path)}
# Create a basic config with test-project like unit tests do
projects = {"test-project": str(config_home)}
app_config = BasicMemoryConfig(
env="test",
projects=projects,
default_project=test_project.name,
default_project="test-project",
update_permalinks_on_move=True,
)
# Set the module app_config instance project list (like regular tests)
monkeypatch.setattr("basic_memory.config.app_config", app_config)
return app_config
@pytest.fixture
def config_manager(app_config: BasicMemoryConfig, config_home, monkeypatch) -> ConfigManager:
@pytest.fixture(scope="function", autouse=True)
def config_manager(app_config: BasicMemoryConfig, config_home) -> ConfigManager:
config_manager = ConfigManager()
# Update its paths to use the test directory
config_manager.config_dir = config_home / ".basic-memory"
config_manager.config_file = config_manager.config_dir / "config.json"
config_manager.config_dir.mkdir(parents=True, exist_ok=True)
# Override the config directly instead of relying on disk load
config_manager.config = app_config
# Ensure the config file is written to disk
config_manager.save_config(app_config)
# Patch the config_manager in all locations where it's imported
monkeypatch.setattr("basic_memory.config.config_manager", config_manager)
monkeypatch.setattr("basic_memory.services.project_service.config_manager", config_manager)
monkeypatch.setattr("basic_memory.mcp.project_session.config_manager", config_manager)
return config_manager
@pytest.fixture
def project_session(test_project: Project):
# initialize the project session with the test project
basic_memory.mcp.project_session.session.initialize(test_project.name)
@pytest.fixture(scope="function")
def project_config(test_project, monkeypatch):
def project_session(test_project: Project, config_manager):
# Initialize the global session directly with the test project
# This ensures all MCP tools use the test project context
import basic_memory.mcp.project_session
basic_memory.mcp.project_session.session.initialize(test_project.name)
return basic_memory.mcp.project_session.session
@pytest.fixture(scope="function", autouse=True)
def project_config(test_project):
"""Create test project configuration."""
project_config = ProjectConfig(
@@ -168,9 +159,6 @@ def project_config(test_project, monkeypatch):
home=Path(test_project.path),
)
# override config module project config
monkeypatch.setattr("basic_memory.config.config", project_config)
return project_config
@@ -180,6 +168,10 @@ def app(
) -> FastAPI:
"""Create test FastAPI application with single project."""
# Import the FastAPI app AFTER the config_manager has written the test config to disk
# This ensures that when the app's lifespan manager runs, it reads the correct test config
from basic_memory.api.app import app as fastapi_app
app = fastapi_app
app.dependency_overrides[get_project_config] = lambda: project_config
app.dependency_overrides[get_engine_factory] = lambda: engine_factory
@@ -215,7 +207,7 @@ async def search_service(engine_factory, test_project):
@pytest.fixture(scope="function")
def mcp_server(app_config, search_service):
def mcp_server(config_manager, search_service, project_session):
# Import mcp instance
from basic_memory.mcp.server import mcp as server
@@ -225,11 +217,6 @@ def mcp_server(app_config, search_service):
# Import prompts to register them
import basic_memory.mcp.prompts # noqa: F401
# Initialize project session with test project
from basic_memory.mcp.project_session import session
session.initialize(app_config.default_project)
return server
@@ -31,8 +31,8 @@ async def test_build_context_valid_urls(mcp_server, app):
result = await client.call_tool("build_context", {"url": url})
# Should return a valid GraphContext response
assert len(result) == 1
response = result[0].text
assert len(result.content) == 1
response = result.content[0].text
assert '"results"' in response # Should contain results structure
assert '"metadata"' in response # Should contain metadata
@@ -82,6 +82,7 @@ async def test_build_context_empty_urls_fail_validation(mcp_server, app):
or "too_short" in error_message
or "empty or whitespace" in error_message
or "value_error" in error_message
or "should be non-empty" in error_message
)
@@ -101,8 +102,8 @@ async def test_build_context_nonexistent_urls_return_empty_results(mcp_server, a
result = await client.call_tool("build_context", {"url": url})
# Should return valid response with empty results
assert len(result) == 1
response = result[0].text
assert len(result.content) == 1
response = result.content[0].text
assert '"results": []' in response # Empty results
assert '"total_results": 0' in response # Zero count
assert '"metadata"' in response # But should have metadata
@@ -163,8 +164,8 @@ async def test_build_context_pattern_matching_works(mcp_server, app):
# Test pattern matching
result = await client.call_tool("build_context", {"url": "patterns/*"})
assert len(result) == 1
response = result[0].text
assert len(result.content) == 1
response = result.content[0].text
# Should find the pattern matches but not the other note
assert '"total_results": 2' in response or '"primary_count": 2' in response
+37 -29
View File
@@ -31,8 +31,8 @@ async def test_delete_note_by_title(mcp_server, app):
"identifier": "Note to Delete",
},
)
assert len(read_result) == 1
assert "Note to Delete" in read_result[0].text
assert len(read_result.content) == 1
assert "Note to Delete" in read_result.content[0].text
# Delete the note by title
delete_result = await client.call_tool(
@@ -43,9 +43,9 @@ async def test_delete_note_by_title(mcp_server, app):
)
# Should return True for successful deletion
assert len(delete_result) == 1
assert delete_result[0].type == "text"
assert "true" in delete_result[0].text.lower()
assert len(delete_result.content) == 1
assert delete_result.content[0].type == "text"
assert "true" in delete_result.content[0].text.lower()
# Verify the note no longer exists
read_after_delete = await client.call_tool(
@@ -56,8 +56,8 @@ async def test_delete_note_by_title(mcp_server, app):
)
# Should return helpful "Note Not Found" message instead of the actual note
assert len(read_after_delete) == 1
result_text = read_after_delete[0].text
assert len(read_after_delete.content) == 1
result_text = read_after_delete.content[0].text
assert "Note Not Found" in result_text
assert "Note to Delete" in result_text
@@ -87,8 +87,8 @@ async def test_delete_note_by_permalink(mcp_server, app):
)
# Should return True for successful deletion
assert len(delete_result) == 1
assert "true" in delete_result[0].text.lower()
assert len(delete_result.content) == 1
assert "true" in delete_result.content[0].text.lower()
# Verify the note no longer exists by searching
search_result = await client.call_tool(
@@ -99,7 +99,10 @@ async def test_delete_note_by_permalink(mcp_server, app):
)
# Should have no results
assert '"results": []' in search_result[0].text or '"results":[]' in search_result[0].text
assert (
'"results": []' in search_result.content[0].text
or '"results":[]' in search_result.content[0].text
)
@pytest.mark.asyncio
@@ -142,8 +145,8 @@ The system handles multiple projects and users."""
"identifier": "Project Management System",
},
)
assert len(read_result) == 1
result_text = read_result[0].text
assert len(read_result.content) == 1
result_text = read_result.content[0].text
assert "Task tracking functionality" in result_text
assert "depends_on" in result_text
@@ -156,7 +159,7 @@ The system handles multiple projects and users."""
)
# Should return True for successful deletion
assert "true" in delete_result[0].text.lower()
assert "true" in delete_result.content[0].text.lower()
# Verify the note and all its components are deleted
read_after_delete_2 = await client.call_tool(
@@ -167,8 +170,8 @@ The system handles multiple projects and users."""
)
# Should return "Note Not Found" message
assert len(read_after_delete_2) == 1
result_text = read_after_delete_2[0].text
assert len(read_after_delete_2.content) == 1
result_text = read_after_delete_2.content[0].text
assert "Note Not Found" in result_text
assert "Project Management System" in result_text
@@ -209,7 +212,9 @@ async def test_delete_note_special_characters_in_title(mcp_server, app):
)
# Should return True for successful deletion
assert "true" in delete_result[0].text.lower(), f"Failed to delete note: {title}"
assert "true" in delete_result.content[0].text.lower(), (
f"Failed to delete note: {title}"
)
# Verify the note is deleted
read_after_delete = await client.call_tool(
@@ -220,8 +225,8 @@ async def test_delete_note_special_characters_in_title(mcp_server, app):
)
# Should return "Note Not Found" message
assert len(read_after_delete) == 1
result_text = read_after_delete[0].text
assert len(read_after_delete.content) == 1
result_text = read_after_delete.content[0].text
assert "Note Not Found" in result_text
assert title in result_text
@@ -240,8 +245,8 @@ async def test_delete_nonexistent_note(mcp_server, app):
)
# Should return False for unsuccessful deletion
assert len(delete_result) == 1
assert "false" in delete_result[0].text.lower()
assert len(delete_result.content) == 1
assert "false" in delete_result.content[0].text.lower()
@pytest.mark.asyncio
@@ -269,7 +274,7 @@ async def test_delete_note_by_file_path(mcp_server, app):
)
# Should return True for successful deletion
assert "true" in delete_result[0].text.lower()
assert "true" in delete_result.content[0].text.lower()
# Verify deletion
read_after_delete = await client.call_tool(
@@ -280,8 +285,8 @@ async def test_delete_note_by_file_path(mcp_server, app):
)
# Should return "Note Not Found" message
assert len(read_after_delete) == 1
result_text = read_after_delete[0].text
assert len(read_after_delete.content) == 1
result_text = read_after_delete.content[0].text
assert "Note Not Found" in result_text
assert "File Path Delete" in result_text
@@ -311,7 +316,7 @@ async def test_delete_note_case_insensitive(mcp_server, app):
)
# Should return True for successful deletion
assert "true" in delete_result[0].text.lower()
assert "true" in delete_result.content[0].text.lower()
@pytest.mark.asyncio
@@ -349,7 +354,7 @@ async def test_delete_multiple_notes_sequentially(mcp_server, app):
)
# Each deletion should be successful
assert "true" in delete_result[0].text.lower(), f"Failed to delete {title}"
assert "true" in delete_result.content[0].text.lower(), f"Failed to delete {title}"
# Verify all notes are deleted by searching
search_result = await client.call_tool(
@@ -360,7 +365,10 @@ async def test_delete_multiple_notes_sequentially(mcp_server, app):
)
# Should have no results
assert '"results": []' in search_result[0].text or '"results":[]' in search_result[0].text
assert (
'"results": []' in search_result.content[0].text
or '"results":[]' in search_result.content[0].text
)
@pytest.mark.asyncio
@@ -404,7 +412,7 @@ This note contains various Unicode characters:
)
# Should return True for successful deletion
assert "true" in delete_result[0].text.lower()
assert "true" in delete_result.content[0].text.lower()
# Verify deletion
read_after_delete = await client.call_tool(
@@ -415,7 +423,7 @@ This note contains various Unicode characters:
)
# Should return "Note Not Found" message
assert len(read_after_delete) == 1
result_text = read_after_delete[0].text
assert len(read_after_delete.content) == 1
result_text = read_after_delete.content[0].text
assert "Note Not Found" in result_text
assert "Unicode Test Note" in result_text
+28 -28
View File
@@ -35,8 +35,8 @@ async def test_edit_note_append_operation(mcp_server, app):
)
# Should return successful edit summary
assert len(edit_result) == 1
edit_text = edit_result[0].text
assert len(edit_result.content) == 1
edit_text = edit_result.content[0].text
assert "Edited note (append)" in edit_text
assert "Added 5 lines to end of note" in edit_text
assert "test/append-test-note" in edit_text
@@ -49,7 +49,7 @@ async def test_edit_note_append_operation(mcp_server, app):
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "Original content here." in content
assert "## New Section" in content
assert "This content was appended." in content
@@ -82,8 +82,8 @@ async def test_edit_note_prepend_operation(mcp_server, app):
)
# Should return successful edit summary
assert len(edit_result) == 1
edit_text = edit_result[0].text
assert len(edit_result.content) == 1
edit_text = edit_result.content[0].text
assert "Edited note (prepend)" in edit_text
assert "Added 5 lines to beginning of note" in edit_text
@@ -95,7 +95,7 @@ async def test_edit_note_prepend_operation(mcp_server, app):
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "## Important Update" in content
assert "This was added at the top." in content
assert "Existing content." in content
@@ -143,8 +143,8 @@ v1.0.0 introduces new features.""",
)
# Should return successful edit summary
assert len(edit_result) == 1
edit_text = edit_result[0].text
assert len(edit_result.content) == 1
edit_text = edit_result.content[0].text
assert "Edited note (find_replace)" in edit_text
assert "Find and replace operation completed" in edit_text
@@ -156,7 +156,7 @@ v1.0.0 introduces new features.""",
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "v1.2.0" in content
assert "v1.0.0" not in content # Should be completely replaced
assert content.count("v1.2.0") == 3 # Should have exactly 3 occurrences
@@ -206,8 +206,8 @@ All services communicate via message queues.""",
)
# Should return successful edit summary
assert len(edit_result) == 1
edit_text = edit_result[0].text
assert len(edit_result.content) == 1
edit_text = edit_result.content[0].text
assert "Edited note (replace_section)" in edit_text
assert "Replaced content under section '## Implementation'" in edit_text
@@ -219,7 +219,7 @@ All services communicate via message queues.""",
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "New implementation approach using microservices" in content
assert "Old implementation details here" not in content
assert "Service A handles authentication" in content
@@ -282,8 +282,8 @@ Current endpoints include user management."""
)
# Should return edit summary with observation and relation counts
assert len(edit_result) == 1
edit_text = edit_result[0].text
assert len(edit_result.content) == 1
edit_text = edit_result.content[0].text
assert "Edited note (append)" in edit_text
assert "## Observations" in edit_text
assert "## Relations" in edit_text
@@ -301,7 +301,7 @@ Current endpoints include user management."""
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "Added payment processing endpoints" in content
assert "integrates_with [[Payment Gateway]]" in content
@@ -322,8 +322,8 @@ async def test_edit_note_error_handling_note_not_found(mcp_server, app):
)
# Should return helpful error message
assert len(edit_result) == 1
error_text = edit_result[0].text
assert len(edit_result.content) == 1
error_text = edit_result.content[0].text
assert "Edit Failed" in error_text
assert "Non-existent Note" in error_text
assert "search_notes(" in error_text
@@ -357,8 +357,8 @@ async def test_edit_note_error_handling_text_not_found(mcp_server, app):
)
# Should return helpful error message
assert len(edit_result) == 1
error_text = edit_result[0].text
assert len(edit_result.content) == 1
error_text = edit_result.content[0].text
assert "Edit Failed - Text Not Found" in error_text
assert "non-existent text" in error_text
assert "Error Test Note" in error_text
@@ -398,8 +398,8 @@ Final test of the content.""",
)
# Should return helpful error message about count mismatch
assert len(edit_result) == 1
error_text = edit_result[0].text
assert len(edit_result.content) == 1
error_text = edit_result.content[0].text
assert "Edit Failed - Wrong Replacement Count" in error_text
assert "Expected 5 occurrences" in error_text
assert "test" in error_text
@@ -521,8 +521,8 @@ def test_function():
)
# Should successfully handle special characters
assert len(edit_result) == 1
edit_text = edit_result[0].text
assert len(edit_result.content) == 1
edit_text = edit_result.content[0].text
assert "Edited note (append)" in edit_text
assert "## Observations" in edit_text
assert "unicode:" in edit_text
@@ -536,7 +536,7 @@ def test_function():
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "🚀" in content
assert "测试中文" in content
assert "∑∏∂∇∆Ω" in content
@@ -569,7 +569,7 @@ async def test_edit_note_using_different_identifiers(mcp_server, app):
"content": "\n\nEdited by title.",
},
)
assert "Edited note (append)" in edit_result1[0].text
assert "Edited note (append)" in edit_result1.content[0].text
# Test editing by permalink
edit_result2 = await client.call_tool(
@@ -580,7 +580,7 @@ async def test_edit_note_using_different_identifiers(mcp_server, app):
"content": "\n\nEdited by permalink.",
},
)
assert "Edited note (append)" in edit_result2[0].text
assert "Edited note (append)" in edit_result2.content[0].text
# Test editing by folder/title format
edit_result3 = await client.call_tool(
@@ -591,7 +591,7 @@ async def test_edit_note_using_different_identifiers(mcp_server, app):
"content": "\n\nEdited by folder/title.",
},
)
assert "Edited note (append)" in edit_result3[0].text
assert "Edited note (append)" in edit_result3.content[0].text
# Verify all edits were applied
read_result = await client.call_tool(
@@ -601,7 +601,7 @@ async def test_edit_note_using_different_identifiers(mcp_server, app):
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "Edited by title." in content
assert "Edited by permalink." in content
assert "Edited by folder/title." in content
+96 -20
View File
@@ -54,8 +54,8 @@ async def test_list_directory_basic_operation(mcp_server, app):
)
# Should return formatted directory listing
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show the structure
assert "Contents of '/' (depth 1):" in list_text
@@ -113,8 +113,8 @@ async def test_list_directory_specific_folder(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show work folder contents
assert "Contents of '/work' (depth 1):" in list_text
@@ -169,8 +169,8 @@ async def test_list_directory_with_depth(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show nested structure within depth=3
assert "Contents of '/research' (depth 3):" in list_text
@@ -226,8 +226,8 @@ async def test_list_directory_with_glob_pattern(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show only matching files
assert "Files in '/meetings' matching 'Meeting*' (depth 1):" in list_text
@@ -250,8 +250,8 @@ async def test_list_directory_empty_directory(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should indicate no files found
assert "No files found in directory '/empty'" in list_text
@@ -283,8 +283,8 @@ async def test_list_directory_glob_no_matches(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should indicate no matches for the pattern
assert "No files found in directory '/docs' matching '*.py'" in list_text
@@ -325,8 +325,8 @@ async def test_list_directory_various_file_types(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show file names, paths, and titles
assert "📄 Simple Note.md" in list_text
@@ -358,8 +358,8 @@ async def test_list_directory_default_parameters(mcp_server, app):
{}, # Use all defaults
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show root directory with depth 1
assert "Contents of '/' (depth 1):" in list_text
@@ -402,8 +402,8 @@ async def test_list_directory_deep_recursion(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show deep structure
assert "Contents of '/level1' (depth 10):" in list_text
@@ -457,11 +457,87 @@ async def test_list_directory_complex_glob_patterns(mcp_server, app):
},
)
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show only Project files
assert "Project Alpha Plan.md" in list_text
assert "Project Beta Plan.md" in list_text
assert "Meeting Minutes" not in list_text
assert "matching 'Project*'" in list_text
@pytest.mark.asyncio
async def test_list_directory_dot_slash_prefix_paths(mcp_server, app):
"""Test directory listing with ./ prefix paths (reproduces bug report issue)."""
async with Client(mcp_server) as client:
# Create test files in a subdirectory
await client.call_tool(
"write_note",
{
"title": "Artifact One",
"folder": "artifacts",
"content": "# Artifact One\n\nFirst artifact document.",
"tags": "artifact,test",
},
)
await client.call_tool(
"write_note",
{
"title": "Artifact Two",
"folder": "artifacts",
"content": "# Artifact Two\n\nSecond artifact document.",
"tags": "artifact,test",
},
)
# Test normal path without ./ prefix (should work)
normal_result = await client.call_tool(
"list_directory",
{
"dir_name": "artifacts",
"depth": 1,
},
)
assert len(normal_result.content) == 1
normal_text = normal_result.content[0].text
assert "Artifact One.md" in normal_text
assert "Artifact Two.md" in normal_text
assert "2 files" in normal_text
# Test with ./ prefix (this was the failing case in the bug report)
dot_slash_result = await client.call_tool(
"list_directory",
{
"dir_name": "./artifacts",
"depth": 1,
},
)
assert len(dot_slash_result.content) == 1
dot_slash_text = dot_slash_result.content[0].text
# Should show the same files as normal path
assert "Artifact One.md" in dot_slash_text
assert "Artifact Two.md" in dot_slash_text
assert "2 files" in dot_slash_text
# Test with trailing slash after ./ prefix
dot_slash_trailing_result = await client.call_tool(
"list_directory",
{
"dir_name": "./artifacts/",
"depth": 1,
},
)
assert len(dot_slash_trailing_result.content) == 1
dot_slash_trailing_text = dot_slash_trailing_result.content[0].text
# Should show the same files
assert "Artifact One.md" in dot_slash_trailing_text
assert "Artifact Two.md" in dot_slash_trailing_text
assert "2 files" in dot_slash_trailing_text
+43 -87
View File
@@ -34,8 +34,8 @@ async def test_move_note_basic_operation(mcp_server, app):
)
# Should return successful move message
assert len(move_result) == 1
move_text = move_result[0].text
assert len(move_result.content) == 1
move_text = move_result.content[0].text
assert "✅ Note moved successfully" in move_text
assert "Move Test Note" in move_text
assert "destination/moved-note.md" in move_text
@@ -49,7 +49,7 @@ async def test_move_note_basic_operation(mcp_server, app):
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "This note will be moved to a new location" in content
# Verify the original location no longer works
@@ -61,7 +61,7 @@ async def test_move_note_basic_operation(mcp_server, app):
)
# Should return "Note Not Found" message
assert "Note Not Found" in read_original[0].text
assert "Note Not Found" in read_original.content[0].text
@pytest.mark.asyncio
@@ -90,8 +90,8 @@ async def test_move_note_using_permalink(mcp_server, app):
)
# Should successfully move
assert len(move_result) == 1
move_text = move_result[0].text
assert len(move_result.content) == 1
move_text = move_result.content[0].text
assert "✅ Note moved successfully" in move_text
assert "test/permalink-move-test" in move_text
assert "archive/permalink-moved.md" in move_text
@@ -104,7 +104,7 @@ async def test_move_note_using_permalink(mcp_server, app):
},
)
assert "Moving by permalink" in read_result[0].text
assert "Moving by permalink" in read_result.content[0].text
@pytest.mark.asyncio
@@ -150,8 +150,8 @@ This note demonstrates moving complex content."""
)
# Should successfully move
assert len(move_result) == 1
move_text = move_result[0].text
assert len(move_result.content) == 1
move_text = move_result.content[0].text
assert "✅ Note moved successfully" in move_text
assert "Complex Note" in move_text
assert "moved/complex-note.md" in move_text
@@ -164,7 +164,7 @@ This note demonstrates moving complex content."""
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "Has structured observations" in content
assert "implements [[Auth System]]" in content
assert "## Observations" in content
@@ -198,8 +198,8 @@ async def test_move_note_to_nested_directory(mcp_server, app):
)
# Should successfully create directory structure and move
assert len(move_result) == 1
move_text = move_result[0].text
assert len(move_result.content) == 1
move_text = move_result.content[0].text
assert "✅ Note moved successfully" in move_text
assert "Nested Move Test" in move_text
assert "projects/2025/q2/work/nested-note.md" in move_text
@@ -212,7 +212,7 @@ async def test_move_note_to_nested_directory(mcp_server, app):
},
)
assert "This will be moved deep" in read_result[0].text
assert "This will be moved deep" in read_result.content[0].text
@pytest.mark.asyncio
@@ -241,8 +241,8 @@ async def test_move_note_with_special_characters(mcp_server, app):
)
# Should handle special characters properly
assert len(move_result) == 1
move_text = move_result[0].text
assert len(move_result.content) == 1
move_text = move_result.content[0].text
assert "✅ Note moved successfully" in move_text
assert "archive/special-chars-note.md" in move_text
@@ -254,7 +254,7 @@ async def test_move_note_with_special_characters(mcp_server, app):
},
)
assert "Testing special characters in move" in read_result[0].text
assert "Testing special characters in move" in read_result.content[0].text
@pytest.mark.asyncio
@@ -272,8 +272,8 @@ async def test_move_note_error_handling_note_not_found(mcp_server, app):
)
# Should contain error message about the failed operation
assert len(move_result) == 1
error_message = move_result[0].text
assert len(move_result.content) == 1
error_message = move_result.content[0].text
assert "# Move Failed" in error_message
assert "Non-existent Note" in error_message
@@ -304,8 +304,8 @@ async def test_move_note_error_handling_invalid_destination(mcp_server, app):
)
# Should contain error message about the failed operation
assert len(move_result) == 1
error_message = move_result[0].text
assert len(move_result.content) == 1
error_message = move_result.content[0].text
assert "# Move Failed" in error_message
assert "/absolute/path/note.md" in error_message
@@ -347,8 +347,8 @@ async def test_move_note_error_handling_destination_exists(mcp_server, app):
)
# Should contain error message about the failed operation
assert len(move_result) == 1
error_message = move_result[0].text
assert len(move_result.content) == 1
error_message = move_result.content[0].text
assert "# Move Failed" in error_message
assert "already exists" in error_message
@@ -389,8 +389,8 @@ This note contains unique search terms:
},
)
assert len(search_before) > 0
assert "Searchable Note" in search_before[0].text
assert len(search_before.content) > 0
assert "Searchable Note" in search_before.content[0].text
# Move the note
move_result = await client.call_tool(
@@ -401,8 +401,8 @@ This note contains unique search terms:
},
)
assert len(move_result) == 1
move_text = move_result[0].text
assert len(move_result.content) == 1
move_text = move_result.content[0].text
assert "✅ Note moved successfully" in move_text
# Verify note is still searchable after move
@@ -413,8 +413,8 @@ This note contains unique search terms:
},
)
assert len(search_after) > 0
search_text = search_after[0].text
assert len(search_after.content) > 0
search_text = search_after.content[0].text
assert "quantum mechanics" in search_text
assert "research/quantum-ai-note.md" in search_text or "quantum-ai-note" in search_text
@@ -426,7 +426,7 @@ This note contains unique search terms:
},
)
assert len(search_by_path) > 0
assert len(search_by_path.content) > 0
@pytest.mark.asyncio
@@ -473,8 +473,8 @@ async def test_move_note_using_different_identifier_formats(mcp_server, app):
"destination_path": "moved/title-moved.md",
},
)
assert len(move1) == 1
assert "✅ Note moved successfully" in move1[0].text
assert len(move1.content) == 1
assert "✅ Note moved successfully" in move1.content[0].text
# Test moving by permalink
move2 = await client.call_tool(
@@ -484,8 +484,8 @@ async def test_move_note_using_different_identifier_formats(mcp_server, app):
"destination_path": "moved/permalink-moved.md",
},
)
assert len(move2) == 1
assert "✅ Note moved successfully" in move2[0].text
assert len(move2.content) == 1
assert "✅ Note moved successfully" in move2.content[0].text
# Test moving by folder/title format
move3 = await client.call_tool(
@@ -495,18 +495,18 @@ async def test_move_note_using_different_identifier_formats(mcp_server, app):
"destination_path": "moved/folder-title-moved.md",
},
)
assert len(move3) == 1
assert "✅ Note moved successfully" in move3[0].text
assert len(move3.content) == 1
assert "✅ Note moved successfully" in move3.content[0].text
# Verify all notes can be accessed at their new locations
read1 = await client.call_tool("read_note", {"identifier": "moved/title-moved.md"})
assert "Move by title" in read1[0].text
assert "Move by title" in read1.content[0].text
read2 = await client.call_tool("read_note", {"identifier": "moved/permalink-moved.md"})
assert "Move by permalink" in read2[0].text
assert "Move by permalink" in read2.content[0].text
read3 = await client.call_tool("read_note", {"identifier": "moved/folder-title-moved.md"})
assert "Move by folder/title" in read3[0].text
assert "Move by folder/title" in read3.content[0].text
@pytest.mark.asyncio
@@ -545,8 +545,8 @@ async def test_move_note_cross_project_detection(mcp_server, app):
)
# Should detect cross-project attempt and provide helpful guidance
assert len(move_result) == 1
error_message = move_result[0].text
assert len(move_result.content) == 1
error_message = move_result.content[0].text
assert "Cross-Project Move Not Supported" in error_message
assert "test-project-b" in error_message
assert "switch_project" in error_message
@@ -554,50 +554,6 @@ async def test_move_note_cross_project_detection(mcp_server, app):
assert "write_note" in error_message
@pytest.mark.asyncio
async def test_move_note_potential_cross_project_guidance(mcp_server, app):
"""Test guidance for potentially cross-project moves with project-like keywords."""
async with Client(mcp_server) as client:
# Create another test project
await client.call_tool(
"create_memory_project",
{
"project_name": "workspace-docs",
"project_path": "/tmp/workspace-docs",
"set_default": False,
},
)
# Create a note in the default project
await client.call_tool(
"write_note",
{
"title": "Potential Cross Project Note",
"folder": "source",
"content": "# Potential Cross Project Note\n\nThis might be moved cross-project.",
"tags": "test,potential-cross-project",
},
)
# Try to move to a path that contains project-like keywords but not exact project names
move_result = await client.call_tool(
"move_note",
{
"identifier": "Potential Cross Project Note",
"destination_path": "project-archive/moved-note.md",
},
)
# Should provide guidance for potential cross-project moves
assert len(move_result) == 1
error_message = move_result[0].text
assert "Check Project Context" in error_message
assert "workspace-docs" in error_message # Should mention other available projects
assert "list_projects" in error_message
assert "switch_project" in error_message
@pytest.mark.asyncio
async def test_move_note_normal_moves_still_work(mcp_server, app):
"""Test that normal within-project moves still work after cross-project detection."""
@@ -624,8 +580,8 @@ async def test_move_note_normal_moves_still_work(mcp_server, app):
)
# Should work normally
assert len(move_result) == 1
move_text = move_result[0].text
assert len(move_result.content) == 1
move_text = move_result.content[0].text
assert "✅ Note moved successfully" in move_text
assert "Normal Move Note" in move_text
assert "destination/normal-moved.md" in move_text
@@ -638,5 +594,5 @@ async def test_move_note_normal_moves_still_work(mcp_server, app):
},
)
content = read_result[0].text
content = read_result.content[0].text
assert "This should move normally" in content
@@ -20,8 +20,8 @@ async def test_list_projects_basic_operation(mcp_server, app):
)
# Should return formatted project list
assert len(list_result) == 1
list_text = list_result[0].text
assert len(list_result.content) == 1
list_text = list_result.content[0].text
# Should show available projects with status indicators
assert "Available projects:" in list_text
@@ -52,8 +52,8 @@ async def test_get_current_project_operation(mcp_server, app):
{},
)
assert len(current_result) == 1
current_text = current_result[0].text
assert len(current_result.content) == 1
current_text = current_result.content[0].text
# Should show current project and stats
assert "Current project: test-project" in current_text
@@ -114,8 +114,8 @@ This is the second entity.
{},
)
assert len(current_result) == 1
current_text = current_result[0].text
assert len(current_result.content) == 1
current_text = current_result.content[0].text
# Should show entity and observation counts
assert "Current project: test-project" in current_text
@@ -144,8 +144,8 @@ async def test_switch_project_not_found(mcp_server, app):
},
)
assert len(switch_result) == 1
switch_text = switch_result[0].text
assert len(switch_result.content) == 1
switch_text = switch_result.content[0].text
# Should show error message with available projects
assert "Error: Project 'non-existent-project' not found" in switch_text
@@ -166,8 +166,8 @@ async def test_switch_project_to_test_project(mcp_server, app):
},
)
assert len(switch_result) == 1
switch_text = switch_result[0].text
assert len(switch_result.content) == 1
switch_text = switch_result.content[0].text
# Should show successful switch
assert "✓ Switched to test-project project" in switch_text
@@ -189,8 +189,8 @@ async def test_set_default_project_operation(mcp_server, app):
{},
)
assert len(current_result) == 1
current_text = current_result[0].text
assert len(current_result.content) == 1
current_text = current_result.content[0].text
# Should show current project and stats
assert "Current project: test-project" in current_text
@@ -203,8 +203,8 @@ async def test_set_default_project_operation(mcp_server, app):
},
)
assert len(default_result) == 1
default_text = default_result[0].text
assert len(default_result.content) == 1
default_text = default_result.content[0].text
# Should show success message and restart instructions
assert "" in default_text # Success indicator
@@ -245,20 +245,20 @@ async def test_project_management_workflow(mcp_server, app):
async with Client(mcp_server) as client:
# 1. Check current project
current_result = await client.call_tool("get_current_project", {})
assert "test-project" in current_result[0].text
assert "test-project" in current_result.content[0].text
# 2. List all projects
list_result = await client.call_tool("list_memory_projects", {})
assert "Available projects:" in list_result[0].text
assert "test-project" in list_result[0].text
assert "Available projects:" in list_result.content[0].text
assert "test-project" in list_result.content[0].text
# 3. Switch to same project (should work)
switch_result = await client.call_tool("switch_project", {"project_name": "test-project"})
assert "✓ Switched to test-project project" in switch_result[0].text
assert "✓ Switched to test-project project" in switch_result.content[0].text
# 4. Verify we're still on the same project
current_result2 = await client.call_tool("get_current_project", {})
assert "Current project: test-project" in current_result2[0].text
assert "Current project: test-project" in current_result2.content[0].text
@pytest.mark.asyncio
@@ -270,22 +270,22 @@ async def test_project_metadata_consistency(mcp_server, app):
# list_projects
list_result = await client.call_tool("list_memory_projects", {})
assert "Project: test-project" in list_result[0].text
assert "Project: test-project" in list_result.content[0].text
# get_current_project
current_result = await client.call_tool("get_current_project", {})
assert "Project: test-project" in current_result[0].text
assert "Project: test-project" in current_result.content[0].text
# switch_project
switch_result = await client.call_tool("switch_project", {"project_name": "test-project"})
assert "Project: test-project" in switch_result[0].text
assert "Project: test-project" in switch_result.content[0].text
# set_default_project (skip since API not working in test env)
# default_result = await client.call_tool(
# "set_default_project",
# {"project_name": "test-project"}
# )
# assert "Project: test-project" in default_result[0].text
# assert "Project: test-project" in default_result.content[0].text
@pytest.mark.asyncio
@@ -295,7 +295,7 @@ async def test_project_statistics_accuracy(mcp_server, app):
async with Client(mcp_server) as client:
# Get initial stats
initial_result = await client.call_tool("get_current_project", {})
initial_text = initial_result[0].text
initial_text = initial_result.content[0].text
assert initial_text is not None
# Create a new entity
@@ -320,7 +320,7 @@ Testing statistics accuracy.
# Get updated stats
updated_result = await client.call_tool("get_current_project", {})
updated_text = updated_result[0].text
updated_text = updated_result.content[0].text
# Should show project info with stats
assert "Current project: test-project" in updated_text
@@ -357,8 +357,8 @@ async def test_create_project_basic_operation(mcp_server, app):
},
)
assert len(create_result) == 1
create_text = create_result[0].text
assert len(create_result.content) == 1
create_text = create_result.content[0].text
# Should show success message and project details
assert "" in create_text # Success indicator
@@ -371,7 +371,7 @@ async def test_create_project_basic_operation(mcp_server, app):
# Verify project appears in project list
list_result = await client.call_tool("list_memory_projects", {})
list_text = list_result[0].text
list_text = list_result.content[0].text
assert "test-new-project" in list_text
@@ -390,8 +390,8 @@ async def test_create_project_with_default_flag(mcp_server, app):
},
)
assert len(create_result) == 1
create_text = create_result[0].text
assert len(create_result.content) == 1
create_text = create_result.content[0].text
# Should show success and default flag
assert "" in create_text
@@ -401,7 +401,7 @@ async def test_create_project_with_default_flag(mcp_server, app):
# Verify we switched to the new project
current_result = await client.call_tool("get_current_project", {})
current_text = current_result[0].text
current_text = current_result.content[0].text
assert "Current project: test-default-project" in current_text
@@ -455,7 +455,7 @@ async def test_delete_project_basic_operation(mcp_server, app):
# Verify it exists
list_result = await client.call_tool("list_memory_projects", {})
assert "to-be-deleted" in list_result[0].text
assert "to-be-deleted" in list_result.content[0].text
# Delete the project
delete_result = await client.call_tool(
@@ -465,8 +465,8 @@ async def test_delete_project_basic_operation(mcp_server, app):
},
)
assert len(delete_result) == 1
delete_text = delete_result[0].text
assert len(delete_result.content) == 1
delete_text = delete_result.content[0].text
# Should show success message
assert "" in delete_text
@@ -479,7 +479,7 @@ async def test_delete_project_basic_operation(mcp_server, app):
# Verify project no longer appears in list
list_result_after = await client.call_tool("list_memory_projects", {})
assert "to-be-deleted" not in list_result_after[0].text
assert "to-be-deleted" not in list_result_after.content[0].text
@pytest.mark.asyncio
@@ -546,8 +546,8 @@ async def test_project_lifecycle_workflow(mcp_server, app):
"project_path": project_path,
},
)
assert "" in create_result[0].text
assert project_name in create_result[0].text
assert "" in create_result.content[0].text
assert project_name in create_result.content[0].text
# 2. Switch to the new project
switch_result = await client.call_tool(
@@ -556,7 +556,7 @@ async def test_project_lifecycle_workflow(mcp_server, app):
"project_name": project_name,
},
)
assert f"✓ Switched to {project_name} project" in switch_result[0].text
assert f"✓ Switched to {project_name} project" in switch_result.content[0].text
# 3. Create content in the new project
await client.call_tool(
@@ -571,7 +571,7 @@ async def test_project_lifecycle_workflow(mcp_server, app):
# 4. Verify project stats show our content
current_result = await client.call_tool("get_current_project", {})
current_text = current_result[0].text
current_text = current_result.content[0].text
assert f"Current project: {project_name}" in current_text
assert "entities" in current_text
@@ -590,13 +590,13 @@ async def test_project_lifecycle_workflow(mcp_server, app):
"project_name": project_name,
},
)
assert "" in delete_result[0].text
assert f"{project_name}" in delete_result[0].text
assert "removed successfully" in delete_result[0].text
assert "" in delete_result.content[0].text
assert f"{project_name}" in delete_result.content[0].text
assert "removed successfully" in delete_result.content[0].text
# 7. Verify project is gone from list
list_result = await client.call_tool("list_memory_projects", {})
assert project_name not in list_result[0].text
assert project_name not in list_result.content[0].text
@pytest.mark.asyncio
@@ -604,23 +604,23 @@ async def test_create_delete_project_edge_cases(mcp_server, app):
"""Test edge cases for create and delete project operations."""
async with Client(mcp_server) as client:
# Test with special characters in project name (should be handled gracefully)
special_name = "test-project-with-dashes"
# Test with special characters and spaces in project name (should be handled gracefully)
special_name = "test project with spaces & symbols!"
# Create project with special characters
create_result = await client.call_tool(
"create_memory_project",
{
"project_name": special_name,
"project_path": f"/tmp/{special_name}",
"project_path": "/tmp/test-project-with-special-chars",
},
)
assert "" in create_result[0].text
assert special_name in create_result[0].text
assert "" in create_result.content[0].text
assert special_name in create_result.content[0].text
# Verify it appears in list
list_result = await client.call_tool("list_memory_projects", {})
assert special_name in list_result[0].text
assert special_name in list_result.content[0].text
# Delete it
delete_result = await client.call_tool(
@@ -629,12 +629,12 @@ async def test_create_delete_project_edge_cases(mcp_server, app):
"project_name": special_name,
},
)
assert "" in delete_result[0].text
assert special_name in delete_result[0].text
assert "" in delete_result.content[0].text
assert special_name in delete_result.content[0].text
# Verify it's gone
list_result_after = await client.call_tool("list_memory_projects", {})
assert special_name not in list_result_after[0].text
assert special_name not in list_result_after.content[0].text
@pytest.mark.asyncio
@@ -651,12 +651,12 @@ async def test_case_insensitive_project_switching(mcp_server, app):
"project_path": f"/tmp/{project_name}",
},
)
assert "" in create_result[0].text
assert project_name in create_result[0].text
assert "" in create_result.content[0].text
assert project_name in create_result.content[0].text
# Verify project was created with canonical name
list_result = await client.call_tool("list_memory_projects", {})
assert project_name in list_result[0].text
assert project_name in list_result.content[0].text
# Test switching with different case variations
test_cases = [
@@ -674,18 +674,18 @@ async def test_case_insensitive_project_switching(mcp_server, app):
)
# Should succeed and show canonical name in response
assert "✓ Switched to" in switch_result[0].text
assert project_name in switch_result[0].text # Canonical name should appear
assert "✓ Switched to" in switch_result.content[0].text
assert project_name in switch_result.content[0].text # Canonical name should appear
# Project summary may be unavailable in test environment
assert (
"Project Summary:" in switch_result[0].text
or "Project summary unavailable" in switch_result[0].text
"Project Summary:" in switch_result.content[0].text
or "Project summary unavailable" in switch_result.content[0].text
)
# Verify get_current_project works after case-insensitive switch
try:
current_result = await client.call_tool("get_current_project", {})
current_text = current_result[0].text
current_text = current_result.content[0].text
# Should show canonical project name, not the input case
assert f"Current project: {project_name}" in current_text
@@ -715,15 +715,15 @@ async def test_case_insensitive_project_operations(mcp_server, app):
"project_path": f"/tmp/{project_name}",
},
)
assert "" in create_result[0].text
assert "" in create_result.content[0].text
# Switch to project using lowercase input
switch_result = await client.call_tool(
"switch_project",
{"project_name": "camel-case-project"}, # lowercase input
)
assert "✓ Switched to" in switch_result[0].text
assert project_name in switch_result[0].text # Should show canonical name
assert "✓ Switched to" in switch_result.content[0].text
assert project_name in switch_result.content[0].text # Should show canonical name
# Test that MCP operations work correctly after case-insensitive switch
@@ -737,12 +737,12 @@ async def test_case_insensitive_project_operations(mcp_server, app):
"tags": "case,test",
},
)
assert len(write_result) == 1
assert "Case Test Note" in write_result[0].text
assert len(write_result.content) == 1
assert "Case Test Note" in write_result.content[0].text
# 2. Verify get_current_project shows stats correctly
current_result = await client.call_tool("get_current_project", {})
current_text = current_result[0].text
current_text = current_result.content[0].text
assert f"Current project: {project_name}" in current_text
assert "1 entities" in current_text or "entities" in current_text
@@ -751,17 +751,17 @@ async def test_case_insensitive_project_operations(mcp_server, app):
"search_notes",
{"query": "case insensitive"},
)
assert len(search_result) == 1
assert "Case Test Note" in search_result[0].text
assert len(search_result.content) == 1
assert "Case Test Note" in search_result.content[0].text
# 4. Test read_note works
read_result = await client.call_tool(
"read_note",
{"identifier": "Case Test Note"},
)
assert len(read_result) == 1
assert "Case Test Note" in read_result[0].text
assert "case insensitive" in read_result[0].text.lower()
assert len(read_result.content) == 1
assert "Case Test Note" in read_result.content[0].text
assert "case insensitive" in read_result.content[0].text.lower()
# Clean up
await client.call_tool("switch_project", {"project_name": "test-project"})
@@ -788,9 +788,9 @@ async def test_case_insensitive_error_handling(mcp_server, app):
)
# Should show error for all case variations
assert f"Error: Project '{test_case}' not found" in switch_result[0].text
assert "Available projects:" in switch_result[0].text
assert "test-project" in switch_result[0].text
assert f"Error: Project '{test_case}' not found" in switch_result.content[0].text
assert "Available projects:" in switch_result.content[0].text
assert "test-project" in switch_result.content[0].text
@pytest.mark.asyncio
@@ -818,7 +818,7 @@ async def test_case_preservation_in_project_list(mcp_server, app):
# List projects and verify each appears with its original case
list_result = await client.call_tool("list_memory_projects", {})
list_text = list_result[0].text
list_text = list_result.content[0].text
for project_name in test_projects:
assert project_name in list_text, f"Project {project_name} not found in list"
@@ -833,12 +833,12 @@ async def test_case_preservation_in_project_list(mcp_server, app):
)
# Should succeed and show original case in response
assert "✓ Switched to" in switch_result[0].text
assert project_name in switch_result[0].text # Original case preserved
assert "✓ Switched to" in switch_result.content[0].text
assert project_name in switch_result.content[0].text # Original case preserved
# Verify current project shows original case
current_result = await client.call_tool("get_current_project", {})
assert f"Current project: {project_name}" in current_result[0].text
assert f"Current project: {project_name}" in current_result.content[0].text
# Clean up - switch back and delete test projects
await client.call_tool("switch_project", {"project_name": "test-project"})
@@ -888,14 +888,17 @@ async def test_session_state_consistency_after_case_switch(mcp_server, app):
# All operations should work and reference the canonical project name
if op_name == "get_current_project":
assert f"Current project: {project_name}" in result[0].text
assert f"Current project: {project_name}" in result.content[0].text
elif op_name == "list_memory_projects":
assert project_name in result[0].text
assert "(current)" in result[0].text or "current" in result[0].text.lower()
assert project_name in result.content[0].text
assert (
"(current)" in result.content[0].text
or "current" in result.content[0].text.lower()
)
# All operations should include project metadata with canonical name
# FIXME
# assert f"Project: {project_name}" in result[0].text
# assert f"Project: {project_name}" in result.content[0].text
# Clean up
await client.call_tool("switch_project", {"project_name": "test-project"})
@@ -21,8 +21,8 @@ async def test_project_state_sync_after_default_change(mcp_server, app, config_m
async with Client(mcp_server) as client:
# Step 1: Verify initial state - MCP should show test-project as current
initial_result = await client.call_tool("get_current_project", {})
assert len(initial_result) == 1
assert "Current project: test-project" in initial_result[0].text
assert len(initial_result.content) == 1
assert "Current project: test-project" in initial_result.content[0].text
# Step 2: Create a second project that we can switch to
create_result = await client.call_tool(
@@ -33,26 +33,26 @@ async def test_project_state_sync_after_default_change(mcp_server, app, config_m
"set_default": False, # Don't set as default yet
},
)
assert len(create_result) == 1
assert "" in create_result[0].text
assert "minerva" in create_result[0].text
assert len(create_result.content) == 1
assert "" in create_result.content[0].text
assert "minerva" in create_result.content[0].text
# Step 3: Change default project to minerva via set_default_project tool
# This simulates the CLI command `basic-memory project default minerva`
set_default_result = await client.call_tool(
"set_default_project", {"project_name": "minerva"}
)
assert len(set_default_result) == 1
assert "" in set_default_result[0].text
assert "minerva" in set_default_result[0].text
assert len(set_default_result.content) == 1
assert "" in set_default_result.content[0].text
assert "minerva" in set_default_result.content[0].text
# Step 4: Verify MCP session immediately reflects the change (no restart needed)
# This tests the fix - session.refresh_from_config() should have been called
updated_result = await client.call_tool("get_current_project", {})
assert len(updated_result) == 1
assert len(updated_result.content) == 1
# The fix should ensure these are consistent now:
updated_text = updated_result[0].text
updated_text = updated_result.content[0].text
assert "Current project: minerva" in updated_text
# Step 5: Verify config manager also shows the new default
@@ -69,14 +69,14 @@ async def test_project_state_sync_after_default_change(mcp_server, app, config_m
"tags": "test,consistency",
},
)
assert len(write_result) == 1
assert "Test Consistency Note" in write_result[0].text
assert len(write_result.content) == 1
assert "Test Consistency Note" in write_result.content[0].text
# Step 7: Test that we can read the note we just created
read_result = await client.call_tool("read_note", {"identifier": "Test Consistency Note"})
assert len(read_result) == 1
assert "Test Consistency Note" in read_result[0].text
assert "project state sync working" in read_result[0].text.lower()
assert len(read_result.content) == 1
assert "Test Consistency Note" in read_result.content[0].text
assert "project state sync working" in read_result.content[0].text.lower()
# Step 8: Test that edit operations work (this was failing in the original issue)
edit_result = await client.call_tool(
@@ -87,15 +87,18 @@ async def test_project_state_sync_after_default_change(mcp_server, app, config_m
"content": "\n\n## Update\n\nEdit operation successful after project switch!",
},
)
assert len(edit_result) == 1
assert "added" in edit_result[0].text.lower() and "lines" in edit_result[0].text.lower()
assert len(edit_result.content) == 1
assert (
"added" in edit_result.content[0].text.lower()
and "lines" in edit_result.content[0].text.lower()
)
# Step 9: Verify the edit was applied
final_read_result = await client.call_tool(
"read_note", {"identifier": "Test Consistency Note"}
)
assert len(final_read_result) == 1
final_content = final_read_result[0].text
assert len(final_read_result.content) == 1
final_content = final_read_result.content[0].text
assert "Edit operation successful" in final_content
# Clean up - switch back to test-project
@@ -124,11 +127,11 @@ async def test_multiple_project_switches_maintain_consistency(mcp_server, app, c
set_result = await client.call_tool(
"set_default_project", {"project_name": project_name}
)
assert "" in set_result[0].text
assert "" in set_result.content[0].text
# Verify MCP session immediately reflects the change
current_result = await client.call_tool("get_current_project", {})
assert f"Current project: {project_name}" in current_result[0].text
assert f"Current project: {project_name}" in current_result.content[0].text
# Verify config is also updated
assert config_manager.default_project == project_name
@@ -144,7 +147,7 @@ async def test_multiple_project_switches_maintain_consistency(mcp_server, app, c
"tags": "test",
},
)
assert note_title in write_result[0].text
assert note_title in write_result.content[0].text
# Clean up - switch back to test-project
await client.call_tool("set_default_project", {"project_name": "test-project"})
@@ -159,8 +162,8 @@ async def test_session_handles_nonexistent_project_gracefully(mcp_server, app):
switch_result = await client.call_tool(
"switch_project", {"project_name": "nonexistent-project"}
)
assert len(switch_result) == 1
result_text = switch_result[0].text
assert len(switch_result.content) == 1
result_text = switch_result.content[0].text
# Should show an error message
assert "Error:" in result_text
@@ -170,4 +173,4 @@ async def test_session_handles_nonexistent_project_gracefully(mcp_server, app):
# Verify the session stays on the original project
current_result = await client.call_tool("get_current_project", {})
assert "Current project: test-project" in current_result[0].text
assert "Current project: test-project" in current_result.content[0].text
@@ -13,9 +13,9 @@ from fastmcp.exceptions import ToolError
def parse_read_content_response(mcp_result):
"""Helper function to parse read_content MCP response."""
assert len(mcp_result) == 1
assert mcp_result[0].type == "text"
return json.loads(mcp_result[0].text)
assert len(mcp_result.content) == 1
assert mcp_result.content[0].type == "text"
return json.loads(mcp_result.content[0].text)
@pytest.mark.asyncio
@@ -359,9 +359,9 @@ async def test_read_content_special_characters_in_filename(mcp_server, app):
},
)
assert len(read_result) == 1
assert read_result[0].type == "text"
content = read_result[0].text
assert len(read_result.content) == 1
assert read_result.content[0].type == "text"
content = read_result.content[0].text
assert f"# {title}" in content
assert f"Content for {title}" in content
+6 -6
View File
@@ -24,9 +24,9 @@ async def test_read_note_after_write(mcp_server, app):
},
)
assert len(write_result) == 1
assert write_result[0].type == "text"
assert "Test Note.md" in write_result[0].text
assert len(write_result.content) == 1
assert write_result.content[0].type == "text"
assert "Test Note.md" in write_result.content[0].text
# Then read it back
read_result = await client.call_tool(
@@ -36,9 +36,9 @@ async def test_read_note_after_write(mcp_server, app):
},
)
assert len(read_result) == 1
assert read_result[0].type == "text"
result_text = read_result[0].text
assert len(read_result.content) == 1
assert read_result.content[0].type == "text"
result_text = read_result.content[0].text
# Should contain the note content and metadata
assert "# Test Note" in result_text
+15 -15
View File
@@ -53,11 +53,11 @@ async def test_search_basic_text_search(mcp_server, app):
},
)
assert len(search_result) == 1
assert search_result[0].type == "text"
assert len(search_result.content) == 1
assert search_result.content[0].type == "text"
# Parse the response (it should be a SearchResponse)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Python Programming Guide" in result_text
assert "Flask Web Development" in result_text
assert "JavaScript Basics" not in result_text
@@ -107,7 +107,7 @@ async def test_search_boolean_operators(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Python Flask Tutorial" in result_text
assert "Python Django Guide" not in result_text
assert "React JavaScript" not in result_text
@@ -120,7 +120,7 @@ async def test_search_boolean_operators(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Python Flask Tutorial" in result_text
assert "Python Django Guide" in result_text
assert "React JavaScript" not in result_text
@@ -133,7 +133,7 @@ async def test_search_boolean_operators(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Python Flask Tutorial" in result_text
assert "Python Django Guide" not in result_text
@@ -173,7 +173,7 @@ async def test_search_title_only(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Database Design" in result_text
assert "Web Development" not in result_text # Has "database" in content but not title
@@ -213,7 +213,7 @@ async def test_search_permalink_exact(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "API Documentation" in result_text
assert "API Testing" not in result_text
@@ -263,7 +263,7 @@ async def test_search_permalink_pattern(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Meeting Notes January" in result_text
assert "Meeting Notes February" in result_text
assert "Project Notes" not in result_text
@@ -308,7 +308,7 @@ Regular content about development practices."""
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
# Should find the main entity but filter out observations/relations
assert "Development Process" in result_text
@@ -340,7 +340,7 @@ async def test_search_pagination(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
# Should contain 5 results and pagination info
assert '"current_page": 1' in result_text
assert '"page_size": 5' in result_text
@@ -355,7 +355,7 @@ async def test_search_pagination(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert '"current_page": 2' in result_text
@@ -383,7 +383,7 @@ async def test_search_no_results(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert '"results": []' in result_text or '"results":[]' in result_text
@@ -431,7 +431,7 @@ async def test_search_complex_boolean_query(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Python Web Development" in result_text
assert "JavaScript Web Development" in result_text
assert "Python Data Science" not in result_text # Has Python but not web
@@ -464,5 +464,5 @@ async def test_search_case_insensitive(mcp_server, app):
},
)
result_text = search_result[0].text
result_text = search_result.content[0].text
assert "Machine Learning Guide" in result_text, f"Failed for search term: {search_term}"

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