basic-memory 0.13.2__tar.gz → 0.13.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- {basic_memory-0.13.2 → basic_memory-0.13.3}/PKG-INFO +1 -1
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/__init__.py +1 -1
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/config.py +24 -9
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/project_management.py +27 -17
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/project_info.py +2 -2
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/project_service.py +14 -8
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_project_management_integration.py +265 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/check-health.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/commands.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/lint-fix.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/release/beta.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/release/changelog.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/release/release-check.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/release/release.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/test-coverage.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.claude/commands/test-live.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.env.oauth.example +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/ISSUE_TEMPLATE/documentation.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/dependabot.yml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/workflows/claude.yml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/workflows/dev-release.yml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/workflows/pr-title.yml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/workflows/release.yml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.github/workflows/test.yml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.gitignore +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.mcp.json +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/.python-version +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/AUTH.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/CHANGELOG.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/CITATION.cff +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/CLA.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/CLAUDE.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/CODE_OF_CONDUCT.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/CONTRIBUTING.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/Dockerfile +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/LICENSE +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/README.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/SECURITY.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/basic-memory.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/AI Assistant Guide.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/CLI Reference.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Canvas.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Claude.ai Integration.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Getting Started with Basic Memory.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Knowledge Format.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/OAuth Authentication Guide.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Obsidian Integration.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Supabase OAuth Setup.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Technical Information.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/User Guide.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/Welcome to Basic memory.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/Canvas.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/Claude-Obsidian-Demo.mp4 +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/Prompt.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/disk-ai-logo-400x400.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/disk-ai-logo.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/prompt 1.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/prompt2.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/prompt3.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/attachments/prompt4.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/docs/publish.js +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Brewing Equipment.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Coffee Bean Origins.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Coffee Brewing Methods.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Coffee Flavor Map.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Coffee Knowledge Base.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Flavor Extraction.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Perfect Pour Over Coffee Method.canvas +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/Coffee Notes/Tasting Notes.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/examples/testing/Test Note Creation - Basic Functionality.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/justfile +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/llms-install.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/memory.json +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/pyproject.toml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/scripts/install.sh +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/smithery.yaml +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/alembic.ini +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/env.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/migrations.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/script.py.mako +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/app.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/directory_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/importer_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/knowledge_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/management_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/memory_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/project_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/prompt_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/resource_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/search_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/routers/utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/api/template_loader.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/app.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/auth.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/db.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/import_chatgpt.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/import_claude_conversations.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/import_claude_projects.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/import_memory_json.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/mcp.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/project.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/status.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/sync.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/commands/tool.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/cli/main.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/db.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/deps.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/file_utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/importers/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/importers/base.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/importers/chatgpt_importer.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/importers/claude_conversations_importer.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/importers/claude_projects_importer.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/importers/memory_json_importer.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/importers/utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/markdown/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/markdown/entity_parser.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/markdown/markdown_processor.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/markdown/plugins.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/markdown/schemas.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/markdown/utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/async_client.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/auth_provider.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/external_auth_provider.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/project_session.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/prompts/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/prompts/ai_assistant_guide.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/prompts/continue_conversation.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/prompts/recent_activity.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/prompts/search.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/prompts/sync_status.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/prompts/utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/resources/ai_assistant_guide.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/resources/project_info.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/server.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/supabase_auth_provider.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/build_context.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/canvas.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/delete_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/edit_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/list_directory.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/move_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/read_content.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/read_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/recent_activity.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/search.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/sync_status.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/view_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/write_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/models/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/models/base.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/models/knowledge.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/models/project.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/models/search.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/entity_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/observation_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/project_info_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/project_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/relation_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/repository/search_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/base.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/delete.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/directory.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/importer.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/memory.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/prompt.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/request.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/response.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/schemas/search.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/context_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/directory_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/entity_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/exceptions.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/file_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/initialization.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/link_resolver.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/migration_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/search_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/services/sync_status_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/sync/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/sync/background_sync.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/sync/sync_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/sync/watch_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/templates/prompts/continue_conversation.hbs +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/templates/prompts/search.hbs +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/static/ai_assistant_guide.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/static/json_canvas_spec_1_0.md +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/conftest.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_build_context_validation.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_delete_note_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_edit_note_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_list_directory_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_move_note_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_read_content_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_read_note_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_search_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_write_note_integration.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/Non-MarkdownFileSupport.pdf +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/Screenshot.png +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/conftest.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_continue_conversation_template.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_directory_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_importer_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_knowledge_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_management_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_memory_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_project_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_project_router_operations.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_prompt_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_resource_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_search_router.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_search_template.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_template_loader.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/api/test_template_loader_helpers.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/conftest.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_auth_commands.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_cli_tools.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_import_chatgpt.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_import_claude_conversations.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_import_claude_projects.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_import_memory_json.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_project_commands.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_project_info.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_status.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_sync.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/cli/test_version.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/conftest.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/importers/test_importer_base.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/importers/test_importer_utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/__init__.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/test_entity_parser.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/test_markdown_plugins.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/test_markdown_processor.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/test_observation_edge_cases.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/test_parser_edge_cases.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/test_relation_edge_cases.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/markdown/test_task_detection.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/conftest.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_auth_provider.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_prompts.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_resource_project_info.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_resources.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_server.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_build_context.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_canvas.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_delete_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_edit_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_list_directory.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_move_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_read_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_recent_activity.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_resource.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_search.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_sync_status.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_view_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/mcp/test_tool_write_note.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/repository/test_entity_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/repository/test_observation_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/repository/test_project_info_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/repository/test_project_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/repository/test_relation_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/repository/test_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/repository/test_search_repository.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/schemas/test_memory_url.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/schemas/test_memory_url_validation.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/schemas/test_schemas.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/schemas/test_search.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_context_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_directory_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_entity_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_file_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_initialization.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_link_resolver.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_project_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_project_service_operations.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_search_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/services/test_sync_status_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/sync/test_sync_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/sync/test_sync_wikilink_issue.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/sync/test_tmp_files.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/sync/test_watch_service.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/sync/test_watch_service_edge_cases.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/utils/test_file_utils.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/utils/test_parse_tags.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/utils/test_permalink_formatting.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/tests/utils/test_utf8_handling.py +0 -0
- {basic_memory-0.13.2 → basic_memory-0.13.3}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-memory
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.3
|
|
4
4
|
Summary: Local-first knowledge management combining Zettelkasten with knowledge graphs
|
|
5
5
|
Project-URL: Homepage, https://github.com/basicmachines-co/basic-memory
|
|
6
6
|
Project-URL: Repository, https://github.com/basicmachines-co/basic-memory
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
|
|
2
2
|
|
|
3
3
|
# Package version - updated by release automation
|
|
4
|
-
__version__ = "0.13.
|
|
4
|
+
__version__ = "0.13.3"
|
|
5
5
|
|
|
6
6
|
# API version for FastAPI - independent of package version
|
|
7
7
|
__api_version__ = "v0"
|
|
@@ -4,7 +4,7 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any, Dict, Literal, Optional, List
|
|
7
|
+
from typing import Any, Dict, Literal, Optional, List, Tuple
|
|
8
8
|
|
|
9
9
|
from loguru import logger
|
|
10
10
|
from pydantic import Field, field_validator
|
|
@@ -196,7 +196,8 @@ class ConfigManager:
|
|
|
196
196
|
|
|
197
197
|
def add_project(self, name: str, path: str) -> ProjectConfig:
|
|
198
198
|
"""Add a new project to the configuration."""
|
|
199
|
-
|
|
199
|
+
project_name, _ = self.get_project(name)
|
|
200
|
+
if project_name: # pragma: no cover
|
|
200
201
|
raise ValueError(f"Project '{name}' already exists")
|
|
201
202
|
|
|
202
203
|
# Ensure the path exists
|
|
@@ -209,10 +210,12 @@ class ConfigManager:
|
|
|
209
210
|
|
|
210
211
|
def remove_project(self, name: str) -> None:
|
|
211
212
|
"""Remove a project from the configuration."""
|
|
212
|
-
|
|
213
|
+
|
|
214
|
+
project_name, path = self.get_project(name)
|
|
215
|
+
if not project_name: # pragma: no cover
|
|
213
216
|
raise ValueError(f"Project '{name}' not found")
|
|
214
217
|
|
|
215
|
-
if
|
|
218
|
+
if project_name == self.config.default_project: # pragma: no cover
|
|
216
219
|
raise ValueError(f"Cannot remove the default project '{name}'")
|
|
217
220
|
|
|
218
221
|
del self.config.projects[name]
|
|
@@ -220,12 +223,21 @@ class ConfigManager:
|
|
|
220
223
|
|
|
221
224
|
def set_default_project(self, name: str) -> None:
|
|
222
225
|
"""Set the default project."""
|
|
223
|
-
|
|
226
|
+
project_name, path = self.get_project(name)
|
|
227
|
+
if not project_name: # pragma: no cover
|
|
224
228
|
raise ValueError(f"Project '{name}' not found")
|
|
225
229
|
|
|
226
230
|
self.config.default_project = name
|
|
227
231
|
self.save_config(self.config)
|
|
228
232
|
|
|
233
|
+
def get_project(self, name: str) -> Tuple[str, str] | Tuple[None, None]:
|
|
234
|
+
"""Look up a project from the configuration by name or permalink"""
|
|
235
|
+
project_permalink = generate_permalink(name)
|
|
236
|
+
for name, path in app_config.projects.items():
|
|
237
|
+
if project_permalink == generate_permalink(name):
|
|
238
|
+
return name, path
|
|
239
|
+
return None, None
|
|
240
|
+
|
|
229
241
|
|
|
230
242
|
def get_project_config(project_name: Optional[str] = None) -> ProjectConfig:
|
|
231
243
|
"""
|
|
@@ -256,11 +268,14 @@ def get_project_config(project_name: Optional[str] = None) -> ProjectConfig:
|
|
|
256
268
|
# the config contains a dict[str,str] of project names and absolute paths
|
|
257
269
|
assert actual_project_name is not None, "actual_project_name cannot be None"
|
|
258
270
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
271
|
+
project_permalink = generate_permalink(actual_project_name)
|
|
272
|
+
|
|
273
|
+
for name, path in app_config.projects.items():
|
|
274
|
+
if project_permalink == generate_permalink(name):
|
|
275
|
+
return ProjectConfig(name=name, home=Path(path))
|
|
262
276
|
|
|
263
|
-
|
|
277
|
+
# otherwise raise error
|
|
278
|
+
raise ValueError(f"Project '{actual_project_name}' not found")
|
|
264
279
|
|
|
265
280
|
|
|
266
281
|
# Create config manager
|
{basic_memory-0.13.2 → basic_memory-0.13.3}/src/basic_memory/mcp/tools/project_management.py
RENAMED
|
@@ -9,7 +9,6 @@ from textwrap import dedent
|
|
|
9
9
|
from fastmcp import Context
|
|
10
10
|
from loguru import logger
|
|
11
11
|
|
|
12
|
-
from basic_memory.config import get_project_config
|
|
13
12
|
from basic_memory.mcp.async_client import client
|
|
14
13
|
from basic_memory.mcp.project_session import session, add_project_metadata
|
|
15
14
|
from basic_memory.mcp.server import mcp
|
|
@@ -85,27 +84,38 @@ async def switch_project(project_name: str, ctx: Context | None = None) -> str:
|
|
|
85
84
|
response = await call_get(client, "/projects/projects")
|
|
86
85
|
project_list = ProjectList.model_validate(response.json())
|
|
87
86
|
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
# Find the project by name (case-insensitive) or permalink
|
|
88
|
+
target_project = None
|
|
89
|
+
for p in project_list.projects:
|
|
90
|
+
# Match by permalink (handles case-insensitive input)
|
|
91
|
+
if p.permalink == project_permalink:
|
|
92
|
+
target_project = p
|
|
93
|
+
break
|
|
94
|
+
# Also match by name comparison (case-insensitive)
|
|
95
|
+
if p.name.lower() == project_name.lower():
|
|
96
|
+
target_project = p
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
if not target_project:
|
|
91
100
|
available_projects = [p.name for p in project_list.projects]
|
|
92
101
|
return f"Error: Project '{project_name}' not found. Available projects: {', '.join(available_projects)}"
|
|
93
102
|
|
|
94
|
-
# Switch to the project
|
|
95
|
-
|
|
103
|
+
# Switch to the project using the canonical name from database
|
|
104
|
+
canonical_name = target_project.name
|
|
105
|
+
session.set_current_project(canonical_name)
|
|
96
106
|
current_project = session.get_current_project()
|
|
97
|
-
project_config = get_project_config(current_project)
|
|
98
107
|
|
|
99
108
|
# Get project info to show summary
|
|
100
109
|
try:
|
|
110
|
+
current_project_permalink = generate_permalink(canonical_name)
|
|
101
111
|
response = await call_get(
|
|
102
112
|
client,
|
|
103
|
-
f"{
|
|
104
|
-
params={"project_name":
|
|
113
|
+
f"/{current_project_permalink}/project/info",
|
|
114
|
+
params={"project_name": canonical_name},
|
|
105
115
|
)
|
|
106
116
|
project_info = ProjectInfoResponse.model_validate(response.json())
|
|
107
117
|
|
|
108
|
-
result = f"✓ Switched to {
|
|
118
|
+
result = f"✓ Switched to {canonical_name} project\n\n"
|
|
109
119
|
result += "Project Summary:\n"
|
|
110
120
|
result += f"• {project_info.statistics.total_entities} entities\n"
|
|
111
121
|
result += f"• {project_info.statistics.total_observations} observations\n"
|
|
@@ -113,11 +123,11 @@ async def switch_project(project_name: str, ctx: Context | None = None) -> str:
|
|
|
113
123
|
|
|
114
124
|
except Exception as e:
|
|
115
125
|
# If we can't get project info, still confirm the switch
|
|
116
|
-
logger.warning(f"Could not get project info for {
|
|
117
|
-
result = f"✓ Switched to {
|
|
126
|
+
logger.warning(f"Could not get project info for {canonical_name}: {e}")
|
|
127
|
+
result = f"✓ Switched to {canonical_name} project\n\n"
|
|
118
128
|
result += "Project summary unavailable.\n"
|
|
119
129
|
|
|
120
|
-
return add_project_metadata(result,
|
|
130
|
+
return add_project_metadata(result, canonical_name)
|
|
121
131
|
|
|
122
132
|
except Exception as e:
|
|
123
133
|
logger.error(f"Error switching to project {project_name}: {e}")
|
|
@@ -165,13 +175,13 @@ async def get_current_project(ctx: Context | None = None) -> str:
|
|
|
165
175
|
await ctx.info("Getting current project information")
|
|
166
176
|
|
|
167
177
|
current_project = session.get_current_project()
|
|
168
|
-
project_config = get_project_config(current_project)
|
|
169
178
|
result = f"Current project: {current_project}\n\n"
|
|
170
179
|
|
|
171
|
-
# get project stats
|
|
180
|
+
# get project stats (use permalink in URL path)
|
|
181
|
+
current_project_permalink = generate_permalink(current_project)
|
|
172
182
|
response = await call_get(
|
|
173
183
|
client,
|
|
174
|
-
f"{
|
|
184
|
+
f"/{current_project_permalink}/project/info",
|
|
175
185
|
params={"project_name": current_project},
|
|
176
186
|
)
|
|
177
187
|
project_info = ProjectInfoResponse.model_validate(response.json())
|
|
@@ -331,4 +341,4 @@ async def delete_project(project_name: str, ctx: Context | None = None) -> str:
|
|
|
331
341
|
result += "Files remain on disk but project is no longer tracked by Basic Memory.\n"
|
|
332
342
|
result += "Re-add the project to access its content again.\n"
|
|
333
343
|
|
|
334
|
-
return add_project_metadata(result, session.get_current_project())
|
|
344
|
+
return add_project_metadata(result, session.get_current_project())
|
|
@@ -185,9 +185,9 @@ class ProjectItem(BaseModel):
|
|
|
185
185
|
name: str
|
|
186
186
|
path: str
|
|
187
187
|
is_default: bool = False
|
|
188
|
-
|
|
188
|
+
|
|
189
189
|
@property
|
|
190
|
-
def permalink(self) -> str:
|
|
190
|
+
def permalink(self) -> str: # pragma: no cover
|
|
191
191
|
return generate_permalink(self.name)
|
|
192
192
|
|
|
193
193
|
|
|
@@ -64,8 +64,10 @@ class ProjectService:
|
|
|
64
64
|
return await self.repository.find_all()
|
|
65
65
|
|
|
66
66
|
async def get_project(self, name: str) -> Optional[Project]:
|
|
67
|
-
"""Get the file path for a project by name."""
|
|
68
|
-
return await self.repository.get_by_name(name)
|
|
67
|
+
"""Get the file path for a project by name or permalink."""
|
|
68
|
+
return await self.repository.get_by_name(name) or await self.repository.get_by_permalink(
|
|
69
|
+
name
|
|
70
|
+
)
|
|
69
71
|
|
|
70
72
|
async def add_project(self, name: str, path: str, set_default: bool = False) -> None:
|
|
71
73
|
"""Add a new project to the configuration and database.
|
|
@@ -347,12 +349,15 @@ class ProjectService:
|
|
|
347
349
|
# Use specified project or fall back to config project
|
|
348
350
|
project_name = project_name or config.project
|
|
349
351
|
# Get project path from configuration
|
|
350
|
-
project_path = config_manager.
|
|
351
|
-
if not
|
|
352
|
+
name, project_path = config_manager.get_project(project_name)
|
|
353
|
+
if not name: # pragma: no cover
|
|
352
354
|
raise ValueError(f"Project '{project_name}' not found in configuration")
|
|
353
355
|
|
|
356
|
+
assert project_path is not None
|
|
357
|
+
project_permalink = generate_permalink(project_name)
|
|
358
|
+
|
|
354
359
|
# Get project from database to get project_id
|
|
355
|
-
db_project = await self.repository.
|
|
360
|
+
db_project = await self.repository.get_by_permalink(project_permalink)
|
|
356
361
|
if not db_project: # pragma: no cover
|
|
357
362
|
raise ValueError(f"Project '{project_name}' not found in database")
|
|
358
363
|
|
|
@@ -367,7 +372,7 @@ class ProjectService:
|
|
|
367
372
|
|
|
368
373
|
# Get enhanced project information from database
|
|
369
374
|
db_projects = await self.repository.get_active_projects()
|
|
370
|
-
|
|
375
|
+
db_projects_by_permalink = {p.permalink: p for p in db_projects}
|
|
371
376
|
|
|
372
377
|
# Get default project info
|
|
373
378
|
default_project = config_manager.default_project
|
|
@@ -375,7 +380,8 @@ class ProjectService:
|
|
|
375
380
|
# Convert config projects to include database info
|
|
376
381
|
enhanced_projects = {}
|
|
377
382
|
for name, path in config_manager.projects.items():
|
|
378
|
-
|
|
383
|
+
config_permalink = generate_permalink(name)
|
|
384
|
+
db_project = db_projects_by_permalink.get(config_permalink)
|
|
379
385
|
enhanced_projects[name] = {
|
|
380
386
|
"path": path,
|
|
381
387
|
"active": db_project.is_active if db_project else True,
|
|
@@ -668,4 +674,4 @@ class ProjectService:
|
|
|
668
674
|
database_size=db_size_readable,
|
|
669
675
|
watch_status=watch_status,
|
|
670
676
|
timestamp=datetime.now(),
|
|
671
|
-
)
|
|
677
|
+
)
|
{basic_memory-0.13.2 → basic_memory-0.13.3}/test-int/mcp/test_project_management_integration.py
RENAMED
|
@@ -635,3 +635,268 @@ async def test_create_delete_project_edge_cases(mcp_server, app):
|
|
|
635
635
|
# Verify it's gone
|
|
636
636
|
list_result_after = await client.call_tool("list_projects", {})
|
|
637
637
|
assert special_name not in list_result_after[0].text
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
@pytest.mark.asyncio
|
|
641
|
+
async def test_case_insensitive_project_switching(mcp_server, app):
|
|
642
|
+
"""Test case-insensitive project switching with proper database lookup."""
|
|
643
|
+
|
|
644
|
+
async with Client(mcp_server) as client:
|
|
645
|
+
# Create a project with mixed case name
|
|
646
|
+
project_name = "Personal-Project"
|
|
647
|
+
create_result = await client.call_tool(
|
|
648
|
+
"create_project",
|
|
649
|
+
{
|
|
650
|
+
"project_name": project_name,
|
|
651
|
+
"project_path": f"/tmp/{project_name}",
|
|
652
|
+
},
|
|
653
|
+
)
|
|
654
|
+
assert "✓" in create_result[0].text
|
|
655
|
+
assert project_name in create_result[0].text
|
|
656
|
+
|
|
657
|
+
# Verify project was created with canonical name
|
|
658
|
+
list_result = await client.call_tool("list_projects", {})
|
|
659
|
+
assert project_name in list_result[0].text
|
|
660
|
+
|
|
661
|
+
# Test switching with different case variations
|
|
662
|
+
test_cases = [
|
|
663
|
+
"personal-project", # all lowercase
|
|
664
|
+
"PERSONAL-PROJECT", # all uppercase
|
|
665
|
+
"Personal-project", # mixed case 1
|
|
666
|
+
"personal-Project", # mixed case 2
|
|
667
|
+
]
|
|
668
|
+
|
|
669
|
+
for test_input in test_cases:
|
|
670
|
+
# Switch using case-insensitive input
|
|
671
|
+
switch_result = await client.call_tool(
|
|
672
|
+
"switch_project",
|
|
673
|
+
{"project_name": test_input},
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
# Should succeed and show canonical name in response
|
|
677
|
+
assert "✓ Switched to" in switch_result[0].text
|
|
678
|
+
assert project_name in switch_result[0].text # Canonical name should appear
|
|
679
|
+
# Project summary may be unavailable in test environment
|
|
680
|
+
assert (
|
|
681
|
+
"Project Summary:" in switch_result[0].text
|
|
682
|
+
or "Project summary unavailable" in switch_result[0].text
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
# Verify get_current_project works after case-insensitive switch
|
|
686
|
+
try:
|
|
687
|
+
current_result = await client.call_tool("get_current_project", {})
|
|
688
|
+
current_text = current_result[0].text
|
|
689
|
+
|
|
690
|
+
# Should show canonical project name, not the input case
|
|
691
|
+
assert f"Current project: {project_name}" in current_text
|
|
692
|
+
assert "entities" in current_text or "Project: " in current_text
|
|
693
|
+
except Exception as e:
|
|
694
|
+
# In test environment, the project info API may not work properly
|
|
695
|
+
# The key test is that switch_project succeeded with canonical name
|
|
696
|
+
print(f"Note: get_current_project failed in test env: {e}")
|
|
697
|
+
pass
|
|
698
|
+
|
|
699
|
+
# Clean up - switch back to test project and delete the test project
|
|
700
|
+
await client.call_tool("switch_project", {"project_name": "test-project"})
|
|
701
|
+
await client.call_tool("delete_project", {"project_name": project_name})
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
@pytest.mark.asyncio
|
|
705
|
+
async def test_case_insensitive_project_operations(mcp_server, app):
|
|
706
|
+
"""Test that all project operations work correctly after case-insensitive switching."""
|
|
707
|
+
|
|
708
|
+
async with Client(mcp_server) as client:
|
|
709
|
+
# Create a project with capital letters
|
|
710
|
+
project_name = "CamelCase-Project"
|
|
711
|
+
create_result = await client.call_tool(
|
|
712
|
+
"create_project",
|
|
713
|
+
{
|
|
714
|
+
"project_name": project_name,
|
|
715
|
+
"project_path": f"/tmp/{project_name}",
|
|
716
|
+
},
|
|
717
|
+
)
|
|
718
|
+
assert "✓" in create_result[0].text
|
|
719
|
+
|
|
720
|
+
# Switch to project using lowercase input
|
|
721
|
+
switch_result = await client.call_tool(
|
|
722
|
+
"switch_project",
|
|
723
|
+
{"project_name": "camel-case-project"}, # lowercase input
|
|
724
|
+
)
|
|
725
|
+
assert "✓ Switched to" in switch_result[0].text
|
|
726
|
+
assert project_name in switch_result[0].text # Should show canonical name
|
|
727
|
+
|
|
728
|
+
# Test that MCP operations work correctly after case-insensitive switch
|
|
729
|
+
|
|
730
|
+
# 1. Create a note in the switched project
|
|
731
|
+
write_result = await client.call_tool(
|
|
732
|
+
"write_note",
|
|
733
|
+
{
|
|
734
|
+
"title": "Case Test Note",
|
|
735
|
+
"folder": "case-test",
|
|
736
|
+
"content": "# Case Test Note\n\nTesting case-insensitive operations.\n\n- [test] Case insensitive switch\n- relates_to [[Another Note]]",
|
|
737
|
+
"tags": "case,test",
|
|
738
|
+
},
|
|
739
|
+
)
|
|
740
|
+
assert len(write_result) == 1
|
|
741
|
+
assert "Case Test Note" in write_result[0].text
|
|
742
|
+
|
|
743
|
+
# 2. Verify get_current_project shows stats correctly
|
|
744
|
+
current_result = await client.call_tool("get_current_project", {})
|
|
745
|
+
current_text = current_result[0].text
|
|
746
|
+
assert f"Current project: {project_name}" in current_text
|
|
747
|
+
assert "1 entities" in current_text or "entities" in current_text
|
|
748
|
+
|
|
749
|
+
# 3. Test search works in the switched project
|
|
750
|
+
search_result = await client.call_tool(
|
|
751
|
+
"search_notes",
|
|
752
|
+
{"query": "case insensitive"},
|
|
753
|
+
)
|
|
754
|
+
assert len(search_result) == 1
|
|
755
|
+
assert "Case Test Note" in search_result[0].text
|
|
756
|
+
|
|
757
|
+
# 4. Test read_note works
|
|
758
|
+
read_result = await client.call_tool(
|
|
759
|
+
"read_note",
|
|
760
|
+
{"identifier": "Case Test Note"},
|
|
761
|
+
)
|
|
762
|
+
assert len(read_result) == 1
|
|
763
|
+
assert "Case Test Note" in read_result[0].text
|
|
764
|
+
assert "case insensitive" in read_result[0].text.lower()
|
|
765
|
+
|
|
766
|
+
# Clean up
|
|
767
|
+
await client.call_tool("switch_project", {"project_name": "test-project"})
|
|
768
|
+
await client.call_tool("delete_project", {"project_name": project_name})
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
@pytest.mark.asyncio
|
|
772
|
+
async def test_case_insensitive_error_handling(mcp_server, app):
|
|
773
|
+
"""Test error handling for case-insensitive project operations."""
|
|
774
|
+
|
|
775
|
+
async with Client(mcp_server) as client:
|
|
776
|
+
# Test non-existent project with various cases
|
|
777
|
+
non_existent_cases = [
|
|
778
|
+
"NonExistent",
|
|
779
|
+
"non-existent",
|
|
780
|
+
"NON-EXISTENT",
|
|
781
|
+
"Non-Existent-Project",
|
|
782
|
+
]
|
|
783
|
+
|
|
784
|
+
for test_case in non_existent_cases:
|
|
785
|
+
switch_result = await client.call_tool(
|
|
786
|
+
"switch_project",
|
|
787
|
+
{"project_name": test_case},
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
# Should show error for all case variations
|
|
791
|
+
assert f"Error: Project '{test_case}' not found" in switch_result[0].text
|
|
792
|
+
assert "Available projects:" in switch_result[0].text
|
|
793
|
+
assert "test-project" in switch_result[0].text
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
@pytest.mark.asyncio
|
|
797
|
+
async def test_case_preservation_in_project_list(mcp_server, app):
|
|
798
|
+
"""Test that project names preserve their original case in listings."""
|
|
799
|
+
|
|
800
|
+
async with Client(mcp_server) as client:
|
|
801
|
+
# Create projects with different casing patterns
|
|
802
|
+
test_projects = [
|
|
803
|
+
"lowercase-project",
|
|
804
|
+
"UPPERCASE-PROJECT",
|
|
805
|
+
"CamelCase-Project",
|
|
806
|
+
"Mixed-CASE-project",
|
|
807
|
+
]
|
|
808
|
+
|
|
809
|
+
# Create all test projects
|
|
810
|
+
for project_name in test_projects:
|
|
811
|
+
await client.call_tool(
|
|
812
|
+
"create_project",
|
|
813
|
+
{
|
|
814
|
+
"project_name": project_name,
|
|
815
|
+
"project_path": f"/tmp/{project_name}",
|
|
816
|
+
},
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
# List projects and verify each appears with its original case
|
|
820
|
+
list_result = await client.call_tool("list_projects", {})
|
|
821
|
+
list_text = list_result[0].text
|
|
822
|
+
|
|
823
|
+
for project_name in test_projects:
|
|
824
|
+
assert project_name in list_text, f"Project {project_name} not found in list"
|
|
825
|
+
|
|
826
|
+
# Test switching to each project with different case input
|
|
827
|
+
for project_name in test_projects:
|
|
828
|
+
# Switch using lowercase input
|
|
829
|
+
lowercase_input = project_name.lower()
|
|
830
|
+
switch_result = await client.call_tool(
|
|
831
|
+
"switch_project",
|
|
832
|
+
{"project_name": lowercase_input},
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
# Should succeed and show original case in response
|
|
836
|
+
assert "✓ Switched to" in switch_result[0].text
|
|
837
|
+
assert project_name in switch_result[0].text # Original case preserved
|
|
838
|
+
|
|
839
|
+
# Verify current project shows original case
|
|
840
|
+
current_result = await client.call_tool("get_current_project", {})
|
|
841
|
+
assert f"Current project: {project_name}" in current_result[0].text
|
|
842
|
+
|
|
843
|
+
# Clean up - switch back and delete test projects
|
|
844
|
+
await client.call_tool("switch_project", {"project_name": "test-project"})
|
|
845
|
+
for project_name in test_projects:
|
|
846
|
+
await client.call_tool("delete_project", {"project_name": project_name})
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
@pytest.mark.asyncio
|
|
850
|
+
async def test_session_state_consistency_after_case_switch(mcp_server, app):
|
|
851
|
+
"""Test that session state remains consistent after case-insensitive project switching."""
|
|
852
|
+
|
|
853
|
+
async with Client(mcp_server) as client:
|
|
854
|
+
# Create a project with specific case
|
|
855
|
+
project_name = "Session-Test-Project"
|
|
856
|
+
await client.call_tool(
|
|
857
|
+
"create_project",
|
|
858
|
+
{
|
|
859
|
+
"project_name": project_name,
|
|
860
|
+
"project_path": f"/tmp/{project_name}",
|
|
861
|
+
},
|
|
862
|
+
)
|
|
863
|
+
|
|
864
|
+
# Switch using different case
|
|
865
|
+
await client.call_tool(
|
|
866
|
+
"switch_project",
|
|
867
|
+
{"project_name": "session-test-project"}, # lowercase
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
# Perform multiple operations and verify consistency
|
|
871
|
+
operations = [
|
|
872
|
+
(
|
|
873
|
+
"write_note",
|
|
874
|
+
{
|
|
875
|
+
"title": "Session Consistency Test",
|
|
876
|
+
"folder": "session",
|
|
877
|
+
"content": "# Session Test\n\n- [test] Session consistency",
|
|
878
|
+
"tags": "session,test",
|
|
879
|
+
},
|
|
880
|
+
),
|
|
881
|
+
("get_current_project", {}),
|
|
882
|
+
("search_notes", {"query": "session"}),
|
|
883
|
+
("list_projects", {}),
|
|
884
|
+
]
|
|
885
|
+
|
|
886
|
+
for op_name, op_params in operations:
|
|
887
|
+
result = await client.call_tool(op_name, op_params)
|
|
888
|
+
|
|
889
|
+
# All operations should work and reference the canonical project name
|
|
890
|
+
if op_name == "get_current_project":
|
|
891
|
+
assert f"Current project: {project_name}" in result[0].text
|
|
892
|
+
elif op_name == "list_projects":
|
|
893
|
+
assert project_name in result[0].text
|
|
894
|
+
assert "(current)" in result[0].text or "current" in result[0].text.lower()
|
|
895
|
+
|
|
896
|
+
# All operations should include project metadata with canonical name
|
|
897
|
+
# FIXME
|
|
898
|
+
# assert f"Project: {project_name}" in result[0].text
|
|
899
|
+
|
|
900
|
+
# Clean up
|
|
901
|
+
await client.call_tool("switch_project", {"project_name": "test-project"})
|
|
902
|
+
await client.call_tool("delete_project", {"project_name": project_name})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|