basic-memory 0.16.3__tar.gz → 0.17.1__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.
Files changed (389) hide show
  1. {basic_memory-0.16.3 → basic_memory-0.17.1}/CHANGELOG.md +90 -0
  2. {basic_memory-0.16.3 → basic_memory-0.17.1}/CLAUDE.md +75 -12
  3. {basic_memory-0.16.3 → basic_memory-0.17.1}/PKG-INFO +38 -1
  4. {basic_memory-0.16.3 → basic_memory-0.17.1}/README.md +33 -0
  5. {basic_memory-0.16.3 → basic_memory-0.17.1}/justfile +12 -3
  6. {basic_memory-0.16.3 → basic_memory-0.17.1}/pyproject.toml +4 -0
  7. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/__init__.py +1 -1
  8. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/env.py +6 -2
  9. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/f8a9b2c3d4e5_add_pg_trgm_for_fuzzy_link_resolution.py +116 -76
  10. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/app.py +12 -4
  11. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/project_router.py +2 -2
  12. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/knowledge_router.py +1 -3
  13. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/project_router.py +79 -1
  14. basic_memory-0.17.1/src/basic_memory/cli/app.py +84 -0
  15. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/__init__.py +3 -1
  16. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/rclone_commands.py +46 -1
  17. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/command_utils.py +27 -1
  18. basic_memory-0.17.1/src/basic_memory/cli/commands/format.py +198 -0
  19. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/import_chatgpt.py +3 -2
  20. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/import_claude_conversations.py +3 -2
  21. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/import_claude_projects.py +3 -2
  22. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/import_memory_json.py +3 -2
  23. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/mcp.py +5 -25
  24. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/project.py +22 -9
  25. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/status.py +3 -2
  26. basic_memory-0.17.1/src/basic_memory/cli/commands/telemetry.py +81 -0
  27. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/main.py +7 -0
  28. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/config.py +50 -0
  29. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/db.py +16 -0
  30. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/deps.py +20 -8
  31. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/file_utils.py +196 -3
  32. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/importers/utils.py +5 -2
  33. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/markdown/entity_parser.py +6 -0
  34. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/markdown/markdown_processor.py +22 -4
  35. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/async_client.py +1 -0
  36. basic_memory-0.17.1/src/basic_memory/mcp/server.py +81 -0
  37. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/build_context.py +3 -3
  38. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/canvas.py +34 -12
  39. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/chatgpt_tools.py +3 -0
  40. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/delete_note.py +20 -3
  41. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/edit_note.py +7 -3
  42. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/list_directory.py +3 -2
  43. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/move_note.py +16 -10
  44. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/project_management.py +6 -5
  45. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/read_content.py +13 -3
  46. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/read_note.py +24 -12
  47. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/recent_activity.py +4 -4
  48. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/search.py +3 -2
  49. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/utils.py +28 -0
  50. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/view_note.py +2 -1
  51. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/write_note.py +33 -10
  52. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/project_repository.py +13 -1
  53. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/base.py +1 -1
  54. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/v2/__init__.py +5 -1
  55. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/v2/entity.py +34 -1
  56. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/entity_service.py +8 -1
  57. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/file_service.py +32 -5
  58. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/initialization.py +43 -26
  59. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/sync/sync_service.py +36 -2
  60. basic_memory-0.17.1/src/basic_memory/telemetry.py +249 -0
  61. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/utils.py +3 -1
  62. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/conftest.py +9 -0
  63. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_lifespan_shutdown_sync_task_cancellation_integration.py +0 -2
  64. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_project_management_integration.py +48 -22
  65. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_project_state_sync_integration.py +2 -2
  66. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_knowledge_router.py +1 -1
  67. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_project_router.py +83 -0
  68. basic_memory-0.17.1/tests/cli/test_cli_exit.py +48 -0
  69. basic_memory-0.17.1/tests/cli/test_cli_tool_exit.py +85 -0
  70. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_cli_tools.py +6 -6
  71. basic_memory-0.17.1/tests/importers/test_conversation_indexing.py +114 -0
  72. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/importers/test_importer_utils.py +3 -0
  73. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_entity_parser.py +8 -5
  74. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_move_note.py +15 -21
  75. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_read_content.py +124 -93
  76. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_read_note.py +17 -37
  77. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_view_note.py +18 -11
  78. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/schemas/test_schemas.py +42 -0
  79. basic_memory-0.17.1/tests/services/test_initialization.py +122 -0
  80. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_sync_service.py +62 -0
  81. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/test_config.py +113 -0
  82. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/test_rclone_commands.py +172 -6
  83. basic_memory-0.17.1/tests/test_telemetry.py +276 -0
  84. basic_memory-0.17.1/tests/utils/test_file_utils.py +550 -0
  85. {basic_memory-0.16.3 → basic_memory-0.17.1}/uv.lock +135 -3
  86. basic_memory-0.16.3/.claude/agents/python-developer.md +0 -154
  87. basic_memory-0.16.3/.claude/agents/system-architect.md +0 -126
  88. basic_memory-0.16.3/src/basic_memory/cli/app.py +0 -57
  89. basic_memory-0.16.3/src/basic_memory/mcp/server.py +0 -9
  90. basic_memory-0.16.3/tests/services/test_initialization.py +0 -248
  91. basic_memory-0.16.3/tests/utils/test_file_utils.py +0 -216
  92. {basic_memory-0.16.3 → basic_memory-0.17.1}/.claude/commands/release/beta.md +0 -0
  93. {basic_memory-0.16.3 → basic_memory-0.17.1}/.claude/commands/release/changelog.md +0 -0
  94. {basic_memory-0.16.3 → basic_memory-0.17.1}/.claude/commands/release/release-check.md +0 -0
  95. {basic_memory-0.16.3 → basic_memory-0.17.1}/.claude/commands/release/release.md +0 -0
  96. {basic_memory-0.16.3 → basic_memory-0.17.1}/.claude/commands/spec.md +0 -0
  97. {basic_memory-0.16.3 → basic_memory-0.17.1}/.claude/commands/test-live.md +0 -0
  98. {basic_memory-0.16.3 → basic_memory-0.17.1}/.claude/settings.json +0 -0
  99. {basic_memory-0.16.3 → basic_memory-0.17.1}/.dockerignore +0 -0
  100. {basic_memory-0.16.3 → basic_memory-0.17.1}/.env.example +0 -0
  101. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  102. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  103. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/ISSUE_TEMPLATE/documentation.md +0 -0
  104. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  105. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/dependabot.yml +0 -0
  106. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/claude-code-review.yml +0 -0
  107. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/claude-issue-triage.yml +0 -0
  108. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/claude.yml +0 -0
  109. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/dev-release.yml +0 -0
  110. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/docker.yml +0 -0
  111. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/pr-title.yml +0 -0
  112. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/release.yml +0 -0
  113. {basic_memory-0.16.3 → basic_memory-0.17.1}/.github/workflows/test.yml +0 -0
  114. {basic_memory-0.16.3 → basic_memory-0.17.1}/.gitignore +0 -0
  115. {basic_memory-0.16.3 → basic_memory-0.17.1}/.python-version +0 -0
  116. {basic_memory-0.16.3 → basic_memory-0.17.1}/CITATION.cff +0 -0
  117. {basic_memory-0.16.3 → basic_memory-0.17.1}/CLA.md +0 -0
  118. {basic_memory-0.16.3 → basic_memory-0.17.1}/CODE_OF_CONDUCT.md +0 -0
  119. {basic_memory-0.16.3 → basic_memory-0.17.1}/CONTRIBUTING.md +0 -0
  120. {basic_memory-0.16.3 → basic_memory-0.17.1}/Dockerfile +0 -0
  121. {basic_memory-0.16.3 → basic_memory-0.17.1}/LICENSE +0 -0
  122. {basic_memory-0.16.3 → basic_memory-0.17.1}/SECURITY.md +0 -0
  123. {basic_memory-0.16.3 → basic_memory-0.17.1}/docker-compose-postgres.yml +0 -0
  124. {basic_memory-0.16.3 → basic_memory-0.17.1}/docker-compose.yml +0 -0
  125. {basic_memory-0.16.3 → basic_memory-0.17.1}/docs/Docker.md +0 -0
  126. {basic_memory-0.16.3 → basic_memory-0.17.1}/docs/ai-assistant-guide-extended.md +0 -0
  127. {basic_memory-0.16.3 → basic_memory-0.17.1}/docs/character-handling.md +0 -0
  128. {basic_memory-0.16.3 → basic_memory-0.17.1}/docs/cloud-cli.md +0 -0
  129. {basic_memory-0.16.3 → basic_memory-0.17.1}/llms-install.md +0 -0
  130. {basic_memory-0.16.3 → basic_memory-0.17.1}/smithery.yaml +0 -0
  131. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-1 Specification-Driven Development Process.md +0 -0
  132. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-10 Unified Deployment Workflow and Event Tracking.md +0 -0
  133. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-11 Basic Memory API Performance Optimization.md +0 -0
  134. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-12 OpenTelemetry Observability.md +0 -0
  135. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-13 CLI Authentication with Subscription Validation.md +0 -0
  136. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-14 Cloud Git Versioning & GitHub Backup.md +0 -0
  137. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-14- Cloud Git Versioning & GitHub Backup.md +0 -0
  138. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-15 Configuration Persistence via Tigris for Cloud Tenants.md +0 -0
  139. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-16 MCP Cloud Service Consolidation.md +0 -0
  140. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-17 Semantic Search with ChromaDB.md +0 -0
  141. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-18 AI Memory Management Tool.md +0 -0
  142. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-19 Sync Performance and Memory Optimization.md +0 -0
  143. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-2 Slash Commands Reference.md +0 -0
  144. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-20 Simplified Project-Scoped Rclone Sync.md +0 -0
  145. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-3 Agent Definitions.md +0 -0
  146. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-4 Notes Web UI Component Architecture.md +0 -0
  147. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-5 CLI Cloud Upload via WebDAV.md +0 -0
  148. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-6 Explicit Project Parameter Architecture.md +0 -0
  149. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-7 POC to spike Tigris Turso for local access to cloud data.md +0 -0
  150. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-8 TigrisFS Integration.md +0 -0
  151. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-9 Multi-Project Bidirectional Sync Architecture.md +0 -0
  152. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-9 Signed Header Tenant Information.md +0 -0
  153. {basic_memory-0.16.3 → basic_memory-0.17.1}/specs/SPEC-9-1 Follow-Ups- Conflict, Sync, and Observability.md +0 -0
  154. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/alembic.ini +0 -0
  155. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/migrations.py +0 -0
  156. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/script.py.mako +0 -0
  157. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/314f1ea54dc4_add_postgres_full_text_search_support_.py +0 -0
  158. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py +0 -0
  159. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +0 -0
  160. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +0 -0
  161. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +0 -0
  162. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +0 -0
  163. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +0 -0
  164. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/a2b3c4d5e6f7_add_search_index_entity_cascade.py +0 -0
  165. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +0 -0
  166. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +0 -0
  167. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +0 -0
  168. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/__init__.py +0 -0
  169. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/__init__.py +0 -0
  170. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/directory_router.py +0 -0
  171. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/importer_router.py +0 -0
  172. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/knowledge_router.py +0 -0
  173. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/management_router.py +0 -0
  174. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/memory_router.py +0 -0
  175. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/prompt_router.py +0 -0
  176. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/resource_router.py +0 -0
  177. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/search_router.py +0 -0
  178. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/routers/utils.py +0 -0
  179. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/template_loader.py +0 -0
  180. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/__init__.py +0 -0
  181. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/__init__.py +0 -0
  182. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/directory_router.py +0 -0
  183. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/importer_router.py +0 -0
  184. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/memory_router.py +0 -0
  185. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/prompt_router.py +0 -0
  186. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/resource_router.py +0 -0
  187. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/api/v2/routers/search_router.py +0 -0
  188. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/__init__.py +0 -0
  189. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/auth.py +0 -0
  190. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/__init__.py +0 -0
  191. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/api_client.py +0 -0
  192. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/bisync_commands.py +0 -0
  193. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/cloud_utils.py +0 -0
  194. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/core_commands.py +0 -0
  195. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/rclone_config.py +0 -0
  196. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/rclone_installer.py +0 -0
  197. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/upload.py +0 -0
  198. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/cloud/upload_command.py +0 -0
  199. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/db.py +0 -0
  200. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/cli/commands/tool.py +0 -0
  201. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/ignore_utils.py +0 -0
  202. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/importers/__init__.py +0 -0
  203. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/importers/base.py +0 -0
  204. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/importers/chatgpt_importer.py +0 -0
  205. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/importers/claude_conversations_importer.py +0 -0
  206. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/importers/claude_projects_importer.py +0 -0
  207. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/importers/memory_json_importer.py +0 -0
  208. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/markdown/__init__.py +0 -0
  209. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/markdown/plugins.py +0 -0
  210. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/markdown/schemas.py +0 -0
  211. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/markdown/utils.py +0 -0
  212. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/__init__.py +0 -0
  213. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/project_context.py +0 -0
  214. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/prompts/__init__.py +0 -0
  215. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/prompts/ai_assistant_guide.py +0 -0
  216. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/prompts/continue_conversation.py +0 -0
  217. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/prompts/recent_activity.py +0 -0
  218. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/prompts/search.py +0 -0
  219. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/prompts/utils.py +0 -0
  220. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/resources/ai_assistant_guide.md +0 -0
  221. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/resources/project_info.py +0 -0
  222. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/mcp/tools/__init__.py +0 -0
  223. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/models/__init__.py +0 -0
  224. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/models/base.py +0 -0
  225. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/models/knowledge.py +0 -0
  226. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/models/project.py +0 -0
  227. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/models/search.py +0 -0
  228. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/__init__.py +0 -0
  229. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/entity_repository.py +0 -0
  230. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/observation_repository.py +0 -0
  231. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/postgres_search_repository.py +0 -0
  232. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/project_info_repository.py +0 -0
  233. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/relation_repository.py +0 -0
  234. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/repository.py +0 -0
  235. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/search_index_row.py +0 -0
  236. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/search_repository.py +0 -0
  237. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/search_repository_base.py +0 -0
  238. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/repository/sqlite_search_repository.py +0 -0
  239. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/__init__.py +0 -0
  240. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/cloud.py +0 -0
  241. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/delete.py +0 -0
  242. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/directory.py +0 -0
  243. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/importer.py +0 -0
  244. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/memory.py +0 -0
  245. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/project_info.py +0 -0
  246. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/prompt.py +0 -0
  247. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/request.py +0 -0
  248. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/response.py +0 -0
  249. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/search.py +0 -0
  250. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/sync_report.py +0 -0
  251. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/schemas/v2/resource.py +0 -0
  252. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/__init__.py +0 -0
  253. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/context_service.py +0 -0
  254. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/directory_service.py +0 -0
  255. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/exceptions.py +0 -0
  256. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/link_resolver.py +0 -0
  257. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/project_service.py +0 -0
  258. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/search_service.py +0 -0
  259. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/services/service.py +0 -0
  260. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/sync/__init__.py +0 -0
  261. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/sync/background_sync.py +0 -0
  262. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/sync/watch_service.py +0 -0
  263. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/templates/prompts/continue_conversation.hbs +0 -0
  264. {basic_memory-0.16.3 → basic_memory-0.17.1}/src/basic_memory/templates/prompts/search.hbs +0 -0
  265. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/BENCHMARKS.md +0 -0
  266. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/cli/test_project_commands_integration.py +0 -0
  267. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/cli/test_version_integration.py +0 -0
  268. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_build_context_underscore.py +0 -0
  269. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_build_context_validation.py +0 -0
  270. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_chatgpt_tools_integration.py +0 -0
  271. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_default_project_mode_integration.py +0 -0
  272. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_delete_note_integration.py +0 -0
  273. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_edit_note_integration.py +0 -0
  274. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_list_directory_integration.py +0 -0
  275. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_move_note_integration.py +0 -0
  276. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_read_content_integration.py +0 -0
  277. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_read_note_integration.py +0 -0
  278. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_search_integration.py +0 -0
  279. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_single_project_mcp_integration.py +0 -0
  280. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/mcp/test_write_note_integration.py +0 -0
  281. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/test_db_wal_mode.py +0 -0
  282. {basic_memory-0.16.3 → basic_memory-0.17.1}/test-int/test_disable_permalinks_integration.py +0 -0
  283. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/Non-MarkdownFileSupport.pdf +0 -0
  284. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/README.md +0 -0
  285. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/Screenshot.png +0 -0
  286. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/__init__.py +0 -0
  287. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/conftest.py +0 -0
  288. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_async_client.py +0 -0
  289. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_continue_conversation_template.py +0 -0
  290. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_directory_router.py +0 -0
  291. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_importer_router.py +0 -0
  292. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_knowledge_router.py +0 -0
  293. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_management_router.py +0 -0
  294. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_memory_router.py +0 -0
  295. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_project_router.py +0 -0
  296. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_project_router_operations.py +0 -0
  297. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_prompt_router.py +0 -0
  298. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_relation_background_resolution.py +0 -0
  299. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_resource_router.py +0 -0
  300. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_search_router.py +0 -0
  301. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_search_template.py +0 -0
  302. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_template_loader.py +0 -0
  303. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/test_template_loader_helpers.py +0 -0
  304. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/__init__.py +0 -0
  305. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/conftest.py +0 -0
  306. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_directory_router.py +0 -0
  307. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_importer_router.py +0 -0
  308. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_memory_router.py +0 -0
  309. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_prompt_router.py +0 -0
  310. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_resource_router.py +0 -0
  311. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/api/v2/test_search_router.py +0 -0
  312. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/conftest.py +0 -0
  313. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_cloud_authentication.py +0 -0
  314. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_ignore_utils.py +0 -0
  315. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_import_chatgpt.py +0 -0
  316. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_import_claude_conversations.py +0 -0
  317. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_import_claude_projects.py +0 -0
  318. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_import_memory_json.py +0 -0
  319. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_project_add_with_local_path.py +0 -0
  320. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/cli/test_upload.py +0 -0
  321. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/conftest.py +0 -0
  322. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/db/test_issue_254_foreign_key_constraints.py +0 -0
  323. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/importers/test_importer_base.py +0 -0
  324. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/__init__.py +0 -0
  325. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_date_frontmatter_parsing.py +0 -0
  326. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_entity_parser_error_handling.py +0 -0
  327. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_markdown_plugins.py +0 -0
  328. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_markdown_processor.py +0 -0
  329. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_observation_edge_cases.py +0 -0
  330. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_parser_edge_cases.py +0 -0
  331. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_relation_edge_cases.py +0 -0
  332. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/markdown/test_task_detection.py +0 -0
  333. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/conftest.py +0 -0
  334. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_obsidian_yaml_formatting.py +0 -0
  335. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_permalink_collision_file_overwrite.py +0 -0
  336. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_prompts.py +0 -0
  337. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_resources.py +0 -0
  338. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_build_context.py +0 -0
  339. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_canvas.py +0 -0
  340. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_delete_note.py +0 -0
  341. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_edit_note.py +0 -0
  342. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_list_directory.py +0 -0
  343. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_recent_activity.py +0 -0
  344. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_resource.py +0 -0
  345. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_search.py +0 -0
  346. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_utils.py +0 -0
  347. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_write_note.py +0 -0
  348. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/test_tool_write_note_kebab_filenames.py +0 -0
  349. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/mcp/tools/test_chatgpt_tools.py +0 -0
  350. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_entity_repository.py +0 -0
  351. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_entity_repository_upsert.py +0 -0
  352. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_entity_upsert_issue_187.py +0 -0
  353. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_observation_repository.py +0 -0
  354. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_project_info_repository.py +0 -0
  355. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_project_repository.py +0 -0
  356. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_relation_repository.py +0 -0
  357. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_repository.py +0 -0
  358. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_search_repository.py +0 -0
  359. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/repository/test_search_repository_edit_bug_fix.py +0 -0
  360. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/schemas/test_base_timeframe_minimum.py +0 -0
  361. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/schemas/test_memory_serialization.py +0 -0
  362. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/schemas/test_memory_url.py +0 -0
  363. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/schemas/test_memory_url_validation.py +0 -0
  364. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/schemas/test_search.py +0 -0
  365. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_context_service.py +0 -0
  366. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_directory_service.py +0 -0
  367. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_entity_service.py +0 -0
  368. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_entity_service_disable_permalinks.py +0 -0
  369. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_file_service.py +0 -0
  370. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_link_resolver.py +0 -0
  371. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_project_removal_bug.py +0 -0
  372. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_project_service.py +0 -0
  373. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_project_service_operations.py +0 -0
  374. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/services/test_search_service.py +0 -0
  375. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_character_conflicts.py +0 -0
  376. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_sync_service_incremental.py +0 -0
  377. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_sync_wikilink_issue.py +0 -0
  378. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_tmp_files.py +0 -0
  379. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_watch_service.py +0 -0
  380. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_watch_service_edge_cases.py +0 -0
  381. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/sync/test_watch_service_reload.py +0 -0
  382. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/test_deps.py +0 -0
  383. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/test_production_cascade_delete.py +0 -0
  384. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/utils/test_frontmatter_obsidian_compatible.py +0 -0
  385. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/utils/test_parse_tags.py +0 -0
  386. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/utils/test_permalink_formatting.py +0 -0
  387. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/utils/test_timezone_utils.py +0 -0
  388. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/utils/test_utf8_handling.py +0 -0
  389. {basic_memory-0.16.3 → basic_memory-0.17.1}/tests/utils/test_validate_project_path.py +0 -0
@@ -1,5 +1,95 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.17.1 (2025-12-29)
4
+
5
+ ### Bug Fixes
6
+
7
+ - **#482**: Only set BASIC_MEMORY_ENV=test during pytest runs
8
+ ([`98fbd60`](https://github.com/basicmachines-co/basic-memory/commit/98fbd60))
9
+ - Fixes environment variable pollution affecting alembic migrations
10
+ - Test environment detection now scoped to pytest execution only
11
+
12
+ ## v0.17.0 (2025-12-28)
13
+
14
+ ### Features
15
+
16
+ - **#478**: Add anonymous usage telemetry with Homebrew-style opt-out
17
+ ([`856737f`](https://github.com/basicmachines-co/basic-memory/commit/856737f))
18
+ - Privacy-respecting anonymous usage analytics
19
+ - Easy opt-out via `BASIC_MEMORY_NO_ANALYTICS=1` environment variable
20
+ - Helps improve Basic Memory based on real usage patterns
21
+
22
+ - **#474**: Add auto-format files on save with built-in Python formatter
23
+ ([`1fd680c`](https://github.com/basicmachines-co/basic-memory/commit/1fd680c))
24
+ - Automatic markdown formatting on file save
25
+ - Built-in Python formatter for consistent code style
26
+ - Configurable formatting options
27
+
28
+ - **#447**: Complete Phase 2 of API v2 migration - MCP tools use v2 endpoints
29
+ ([`1a74d85`](https://github.com/basicmachines-co/basic-memory/commit/1a74d85))
30
+ - All MCP tools now use optimized v2 API endpoints
31
+ - Improved performance for knowledge graph operations
32
+ - Foundation for future API enhancements
33
+
34
+ ### Bug Fixes
35
+
36
+ - Fix UTF-8 BOM handling in frontmatter parsing
37
+ ([`85684f8`](https://github.com/basicmachines-co/basic-memory/commit/85684f8))
38
+ - Handles files with UTF-8 byte order marks correctly
39
+ - Prevents frontmatter parsing failures
40
+
41
+ - **#475**: Handle null titles in ChatGPT import
42
+ ([`14ce5a3`](https://github.com/basicmachines-co/basic-memory/commit/14ce5a3))
43
+ - Gracefully handles conversations without titles
44
+ - Improved import robustness
45
+
46
+ - Remove MaxLen constraint from observation content
47
+ ([`45d6caf`](https://github.com/basicmachines-co/basic-memory/commit/45d6caf))
48
+ - Allows longer observation content without truncation
49
+ - Removes arbitrary 2000 character limit
50
+
51
+ - Handle FileNotFoundError gracefully during sync
52
+ ([`1652f86`](https://github.com/basicmachines-co/basic-memory/commit/1652f86))
53
+ - Prevents sync failures when files are deleted during sync
54
+ - More resilient file watching
55
+
56
+ - Use canonical project names in API response messages
57
+ ([`c23927d`](https://github.com/basicmachines-co/basic-memory/commit/c23927d))
58
+ - Consistent project name formatting in all responses
59
+
60
+ - Suppress CLI warnings for cleaner output
61
+ ([`d71c6e8`](https://github.com/basicmachines-co/basic-memory/commit/d71c6e8))
62
+ - Cleaner terminal output without spurious warnings
63
+
64
+ - Prevent DEBUG logs from appearing on CLI stdout
65
+ ([`63b9849`](https://github.com/basicmachines-co/basic-memory/commit/63b9849))
66
+ - Debug logging no longer pollutes CLI output
67
+
68
+ - **#473**: Detect rclone version for --create-empty-src-dirs support
69
+ ([`622d37e`](https://github.com/basicmachines-co/basic-memory/commit/622d37e))
70
+ - Automatic rclone version detection for compatibility
71
+ - Prevents errors on older rclone versions
72
+
73
+ - **#471**: Prevent CLI commands from hanging on exit
74
+ ([`916baf8`](https://github.com/basicmachines-co/basic-memory/commit/916baf8))
75
+ - Fixes CLI hang on shutdown
76
+ - Proper async cleanup
77
+
78
+ - Add cloud_mode check to initialize_app()
79
+ ([`ef7adb7`](https://github.com/basicmachines-co/basic-memory/commit/ef7adb7))
80
+ - Correct initialization for cloud deployments
81
+
82
+ ### Internal
83
+
84
+ - Centralize test environment detection in config.is_test_env
85
+ ([`3cd9178`](https://github.com/basicmachines-co/basic-memory/commit/3cd9178))
86
+ - Unified test environment detection
87
+ - Disables analytics in test environments
88
+
89
+ - Make test-int-postgres compatible with macOS
90
+ ([`95937c6`](https://github.com/basicmachines-co/basic-memory/commit/95937c6))
91
+ - Cross-platform PostgreSQL testing support
92
+
3
93
  ## v0.16.3 (2025-12-20)
4
94
 
5
95
  ### Features
@@ -58,11 +58,69 @@ See the [README.md](README.md) file for a project overview.
58
58
  - Follow the repository pattern for data access
59
59
  - Tools communicate to api routers via the httpx ASGI client (in process)
60
60
 
61
+ ### Code Change Guidelines
62
+
63
+ - **Full file read before edits**: Before editing any file, read it in full first to ensure complete context; partial reads lead to corrupted edits
64
+ - **Minimize diffs**: Prefer the smallest change that satisfies the request. Avoid unrelated refactors or style rewrites unless necessary for correctness
65
+ - **No speculative getattr**: Never use `getattr(obj, "attr", default)` when unsure about attribute names. Check the class definition or source code first
66
+ - **Fail fast**: Write code with fail-fast logic by default. Do not swallow exceptions with errors or warnings
67
+ - **No fallback logic**: Do not add fallback logic unless explicitly told to and agreed with the user
68
+ - **No guessing**: Do not say "The issue is..." before you actually know what the issue is. Investigate first.
69
+
70
+ ### Literate Programming Style
71
+
72
+ Code should tell a story. Comments must explain the "why" and narrative flow, not just the "what".
73
+
74
+ **Section Headers:**
75
+ For files with multiple phases of logic, add section headers so the control flow reads like chapters:
76
+ ```python
77
+ # --- Authentication ---
78
+ # ... auth logic ...
79
+
80
+ # --- Data Validation ---
81
+ # ... validation logic ...
82
+
83
+ # --- Business Logic ---
84
+ # ... core logic ...
85
+ ```
86
+
87
+ **Decision Point Comments:**
88
+ For conditionals that materially change behavior (gates, fallbacks, retries, feature flags), add comments with:
89
+ - **Trigger**: what condition causes this branch
90
+ - **Why**: the rationale (cost, correctness, UX, determinism)
91
+ - **Outcome**: what changes downstream
92
+
93
+ ```python
94
+ # Trigger: project has no active sync watcher
95
+ # Why: avoid duplicate file system watchers consuming resources
96
+ # Outcome: starts new watcher, registers in active_watchers dict
97
+ if project_id not in active_watchers:
98
+ start_watcher(project_id)
99
+ ```
100
+
101
+ **Constraint Comments:**
102
+ If code exists because of a constraint (async requirements, rate limits, schema compatibility), explain the constraint near the code:
103
+ ```python
104
+ # SQLite requires WAL mode for concurrent read/write access
105
+ connection.execute("PRAGMA journal_mode=WAL")
106
+ ```
107
+
108
+ **What NOT to Comment:**
109
+ Avoid comments that restate obvious code:
110
+ ```python
111
+ # Bad - restates code
112
+ counter += 1 # increment counter
113
+
114
+ # Good - explains why
115
+ counter += 1 # track retries for backoff calculation
116
+ ```
117
+
61
118
  ### Codebase Architecture
62
119
 
63
120
  - `/alembic` - Alembic db migrations
64
121
  - `/api` - FastAPI implementation of REST endpoints
65
122
  - `/cli` - Typer command-line interface
123
+ - `/importers` - Import functionality for Claude, ChatGPT, and other sources
66
124
  - `/markdown` - Markdown parsing and processing
67
125
  - `/mcp` - Model Context Protocol server implementation
68
126
  - `/models` - SQLAlchemy ORM models
@@ -140,22 +198,26 @@ See SPEC-16 for full context manager refactor details.
140
198
  ### Basic Memory Commands
141
199
 
142
200
  **Local Commands:**
143
- - Sync knowledge: `basic-memory sync` or `basic-memory sync --watch`
201
+ - Check sync status: `basic-memory status`
144
202
  - Import from Claude: `basic-memory import claude conversations`
145
203
  - Import from ChatGPT: `basic-memory import chatgpt`
146
204
  - Import from Memory JSON: `basic-memory import memory-json`
147
- - Check sync status: `basic-memory status`
148
- - Tool access: `basic-memory tools` (provides CLI access to MCP tools)
149
- - Guide: `basic-memory tools basic-memory-guide`
150
- - Continue: `basic-memory tools continue-conversation --topic="search"`
205
+ - Tool access: `basic-memory tool` (provides CLI access to MCP tools)
206
+ - Continue: `basic-memory tool continue-conversation --topic="search"`
207
+
208
+ **Project Management:**
209
+ - List projects: `basic-memory project list`
210
+ - Add project: `basic-memory project add "name" ~/path`
211
+ - Project info: `basic-memory project info`
212
+ - One-way sync (local -> cloud): `basic-memory project sync`
213
+ - Bidirectional sync: `basic-memory project bisync`
214
+ - Integrity check: `basic-memory project check`
151
215
 
152
216
  **Cloud Commands (requires subscription):**
153
217
  - Authenticate: `basic-memory cloud login`
154
218
  - Logout: `basic-memory cloud logout`
155
- - Bidirectional sync: `basic-memory cloud sync`
156
- - Integrity check: `basic-memory cloud check`
157
- - Mount cloud storage: `basic-memory cloud mount`
158
- - Unmount cloud storage: `basic-memory cloud unmount`
219
+ - Check cloud status: `basic-memory cloud status`
220
+ - Setup cloud sync: `basic-memory cloud setup`
159
221
 
160
222
  ### MCP Capabilities
161
223
 
@@ -182,18 +244,19 @@ See SPEC-16 for full context manager refactor details.
182
244
  - `list_memory_projects()` - List all available projects with their status
183
245
  - `create_memory_project(project_name, project_path, set_default)` - Create new Basic Memory projects
184
246
  - `delete_project(project_name)` - Delete a project from configuration
185
- - `get_current_project()` - Get current project information and stats
186
- - `sync_status()` - Check file synchronization and background operation status
187
247
 
188
248
  **Visualization:**
189
249
  - `canvas(nodes, edges, title, folder)` - Generate Obsidian canvas files for knowledge graph visualization
190
250
 
251
+ **ChatGPT-Compatible Tools:**
252
+ - `search(query)` - Search across knowledge base (OpenAI actions compatible)
253
+ - `fetch(id)` - Fetch full content of a search result document
254
+
191
255
  - MCP Prompts for better AI interaction:
192
256
  - `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
193
257
  - `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
194
258
  - `search(query, after_date)` - Search with detailed, formatted results for better context understanding
195
259
  - `recent_activity(timeframe)` - View recently changed items with formatted output
196
- - `json_canvas_spec()` - Full JSON Canvas specification for Obsidian visualization
197
260
 
198
261
  ### Cloud Features (v0.15.0+)
199
262
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basic-memory
3
- Version: 0.16.3
3
+ Version: 0.17.1
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
@@ -20,7 +20,11 @@ Requires-Dist: greenlet>=3.1.1
20
20
  Requires-Dist: loguru>=0.7.3
21
21
  Requires-Dist: markdown-it-py>=3.0.0
22
22
  Requires-Dist: mcp>=1.2.0
23
+ Requires-Dist: mdformat-frontmatter>=2.0.8
24
+ Requires-Dist: mdformat-gfm>=0.3.7
25
+ Requires-Dist: mdformat>=0.7.22
23
26
  Requires-Dist: nest-asyncio>=1.6.0
27
+ Requires-Dist: openpanel>=0.0.1
24
28
  Requires-Dist: pillow>=11.1.0
25
29
  Requires-Dist: psycopg==3.3.1
26
30
  Requires-Dist: pybars3>=0.9.7
@@ -508,6 +512,39 @@ tail -f ~/.basic-memory/basic-memory.log
508
512
  BASIC_MEMORY_CLOUD_MODE=true uvicorn basic_memory.api.app:app
509
513
  ```
510
514
 
515
+ ## Telemetry
516
+
517
+ Basic Memory collects anonymous usage statistics to help improve the software. This follows the [Homebrew model](https://docs.brew.sh/Analytics) - telemetry is on by default with easy opt-out.
518
+
519
+ **What we collect:**
520
+ - App version, Python version, OS, architecture
521
+ - Feature usage (which MCP tools and CLI commands are used)
522
+ - Error types (sanitized - no file paths or personal data)
523
+
524
+ **What we NEVER collect:**
525
+ - Note content, file names, or paths
526
+ - Personal information
527
+ - IP addresses
528
+
529
+ **Opting out:**
530
+ ```bash
531
+ # Disable telemetry
532
+ basic-memory telemetry disable
533
+
534
+ # Check status
535
+ basic-memory telemetry status
536
+
537
+ # Re-enable
538
+ basic-memory telemetry enable
539
+ ```
540
+
541
+ Or set the environment variable:
542
+ ```bash
543
+ export BASIC_MEMORY_TELEMETRY_ENABLED=false
544
+ ```
545
+
546
+ For more details, see the [Telemetry documentation](https://basicmemory.com/telemetry).
547
+
511
548
  ## Development
512
549
 
513
550
  ### Running Tests
@@ -466,6 +466,39 @@ tail -f ~/.basic-memory/basic-memory.log
466
466
  BASIC_MEMORY_CLOUD_MODE=true uvicorn basic_memory.api.app:app
467
467
  ```
468
468
 
469
+ ## Telemetry
470
+
471
+ Basic Memory collects anonymous usage statistics to help improve the software. This follows the [Homebrew model](https://docs.brew.sh/Analytics) - telemetry is on by default with easy opt-out.
472
+
473
+ **What we collect:**
474
+ - App version, Python version, OS, architecture
475
+ - Feature usage (which MCP tools and CLI commands are used)
476
+ - Error types (sanitized - no file paths or personal data)
477
+
478
+ **What we NEVER collect:**
479
+ - Note content, file names, or paths
480
+ - Personal information
481
+ - IP addresses
482
+
483
+ **Opting out:**
484
+ ```bash
485
+ # Disable telemetry
486
+ basic-memory telemetry disable
487
+
488
+ # Check status
489
+ basic-memory telemetry status
490
+
491
+ # Re-enable
492
+ basic-memory telemetry enable
493
+ ```
494
+
495
+ Or set the environment variable:
496
+ ```bash
497
+ export BASIC_MEMORY_TELEMETRY_ENABLED=false
498
+ ```
499
+
500
+ For more details, see the [Telemetry documentation](https://basicmemory.com/telemetry).
501
+
469
502
  ## Development
470
503
 
471
504
  ### Running Tests
@@ -37,11 +37,11 @@ test-postgres: test-unit-postgres test-int-postgres
37
37
 
38
38
  # Run unit tests against SQLite
39
39
  test-unit-sqlite:
40
- uv run pytest -p pytest_mock -v --no-cov tests
40
+ BASIC_MEMORY_ENV=test uv run pytest -p pytest_mock -v --no-cov tests
41
41
 
42
42
  # Run unit tests against Postgres
43
43
  test-unit-postgres:
44
- BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov tests
44
+ BASIC_MEMORY_ENV=test BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov tests
45
45
 
46
46
  # Run integration tests against SQLite
47
47
  test-int-sqlite:
@@ -51,7 +51,16 @@ test-int-sqlite:
51
51
  # Note: Uses timeout due to FastMCP Client + asyncpg cleanup hang (tests pass, process hangs on exit)
52
52
  # See: https://github.com/jlowin/fastmcp/issues/1311
53
53
  test-int-postgres:
54
- timeout --signal=KILL 600 bash -c 'BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov test-int' || test $? -eq 137
54
+ #!/usr/bin/env bash
55
+ set -euo pipefail
56
+ # Use gtimeout (macOS/Homebrew) or timeout (Linux)
57
+ TIMEOUT_CMD=$(command -v gtimeout || command -v timeout || echo "")
58
+ if [[ -n "$TIMEOUT_CMD" ]]; then
59
+ $TIMEOUT_CMD --signal=KILL 600 bash -c 'BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov test-int' || test $? -eq 137
60
+ else
61
+ echo "⚠️ No timeout command found, running without timeout..."
62
+ BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov test-int
63
+ fi
55
64
 
56
65
  # Reset Postgres test database (drops and recreates schema)
57
66
  # Useful when Alembic migration state gets out of sync during development
@@ -38,6 +38,10 @@ dependencies = [
38
38
  "nest-asyncio>=1.6.0", # For Alembic migrations with Postgres
39
39
  "pytest-asyncio>=1.2.0",
40
40
  "psycopg==3.3.1",
41
+ "mdformat>=0.7.22",
42
+ "mdformat-gfm>=0.3.7",
43
+ "mdformat-frontmatter>=2.0.8",
44
+ "openpanel>=0.0.1", # Anonymous usage telemetry (Homebrew-style opt-out)
41
45
  ]
42
46
 
43
47
 
@@ -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.16.3"
4
+ __version__ = "0.17.1"
5
5
 
6
6
  # API version for FastAPI - independent of package version
7
7
  __api_version__ = "v0"
@@ -21,8 +21,12 @@ from alembic import context
21
21
 
22
22
  from basic_memory.config import ConfigManager
23
23
 
24
- # set config.env to "test" for pytest to prevent logging to file in utils.setup_logging()
25
- os.environ["BASIC_MEMORY_ENV"] = "test"
24
+ # Trigger: only set test env when actually running under pytest
25
+ # Why: alembic/env.py is imported during normal operations (MCP server startup, migrations)
26
+ # but we only want test behavior during actual test runs
27
+ # Outcome: prevents is_test_env from returning True in production, enabling watch service
28
+ if os.getenv("PYTEST_CURRENT_TEST") is not None:
29
+ os.environ["BASIC_MEMORY_ENV"] = "test"
26
30
 
27
31
  # Import after setting environment variable # noqa: E402
28
32
  from basic_memory.models import Base # noqa: E402
@@ -10,6 +10,42 @@ from typing import Sequence, Union
10
10
 
11
11
  import sqlalchemy as sa
12
12
  from alembic import op
13
+ from sqlalchemy import text
14
+
15
+
16
+ def column_exists(connection, table: str, column: str) -> bool:
17
+ """Check if a column exists in a table (idempotent migration support)."""
18
+ if connection.dialect.name == "postgresql":
19
+ result = connection.execute(
20
+ text(
21
+ "SELECT 1 FROM information_schema.columns "
22
+ "WHERE table_name = :table AND column_name = :column"
23
+ ),
24
+ {"table": table, "column": column},
25
+ )
26
+ return result.fetchone() is not None
27
+ else:
28
+ # SQLite
29
+ result = connection.execute(text(f"PRAGMA table_info({table})"))
30
+ columns = [row[1] for row in result]
31
+ return column in columns
32
+
33
+
34
+ def index_exists(connection, index_name: str) -> bool:
35
+ """Check if an index exists (idempotent migration support)."""
36
+ if connection.dialect.name == "postgresql":
37
+ result = connection.execute(
38
+ text("SELECT 1 FROM pg_indexes WHERE indexname = :index_name"),
39
+ {"index_name": index_name},
40
+ )
41
+ return result.fetchone() is not None
42
+ else:
43
+ # SQLite
44
+ result = connection.execute(
45
+ text("SELECT 1 FROM sqlite_master WHERE type='index' AND name = :index_name"),
46
+ {"index_name": index_name},
47
+ )
48
+ return result.fetchone() is not None
13
49
 
14
50
 
15
51
  # revision identifiers, used by Alembic.
@@ -36,101 +72,105 @@ def upgrade() -> None:
36
72
  # Add project_id to relation table
37
73
  # -------------------------------------------------------------------------
38
74
 
39
- # Step 1: Add project_id column as nullable first
40
- op.add_column("relation", sa.Column("project_id", sa.Integer(), nullable=True))
75
+ # Step 1: Add project_id column as nullable first (idempotent)
76
+ if not column_exists(connection, "relation", "project_id"):
77
+ op.add_column("relation", sa.Column("project_id", sa.Integer(), nullable=True))
41
78
 
42
- # Step 2: Backfill project_id from entity.project_id via from_id
43
- if dialect == "postgresql":
44
- op.execute("""
45
- UPDATE relation
46
- SET project_id = entity.project_id
47
- FROM entity
48
- WHERE relation.from_id = entity.id
49
- """)
50
- else:
51
- # SQLite syntax
52
- op.execute("""
53
- UPDATE relation
54
- SET project_id = (
55
- SELECT entity.project_id
79
+ # Step 2: Backfill project_id from entity.project_id via from_id
80
+ if dialect == "postgresql":
81
+ op.execute("""
82
+ UPDATE relation
83
+ SET project_id = entity.project_id
56
84
  FROM entity
57
- WHERE entity.id = relation.from_id
58
- )
59
- """)
60
-
61
- # Step 3: Make project_id NOT NULL and add foreign key
62
- if dialect == "postgresql":
63
- op.alter_column("relation", "project_id", nullable=False)
64
- op.create_foreign_key(
65
- "fk_relation_project_id",
66
- "relation",
67
- "project",
68
- ["project_id"],
69
- ["id"],
70
- )
71
- else:
72
- # SQLite requires batch operations for ALTER COLUMN
73
- with op.batch_alter_table("relation") as batch_op:
74
- batch_op.alter_column("project_id", nullable=False)
75
- batch_op.create_foreign_key(
85
+ WHERE relation.from_id = entity.id
86
+ """)
87
+ else:
88
+ # SQLite syntax
89
+ op.execute("""
90
+ UPDATE relation
91
+ SET project_id = (
92
+ SELECT entity.project_id
93
+ FROM entity
94
+ WHERE entity.id = relation.from_id
95
+ )
96
+ """)
97
+
98
+ # Step 3: Make project_id NOT NULL and add foreign key
99
+ if dialect == "postgresql":
100
+ op.alter_column("relation", "project_id", nullable=False)
101
+ op.create_foreign_key(
76
102
  "fk_relation_project_id",
103
+ "relation",
77
104
  "project",
78
105
  ["project_id"],
79
106
  ["id"],
80
107
  )
81
-
82
- # Step 4: Create index on relation.project_id
83
- op.create_index("ix_relation_project_id", "relation", ["project_id"])
108
+ else:
109
+ # SQLite requires batch operations for ALTER COLUMN
110
+ with op.batch_alter_table("relation") as batch_op:
111
+ batch_op.alter_column("project_id", nullable=False)
112
+ batch_op.create_foreign_key(
113
+ "fk_relation_project_id",
114
+ "project",
115
+ ["project_id"],
116
+ ["id"],
117
+ )
118
+
119
+ # Step 4: Create index on relation.project_id (idempotent)
120
+ if not index_exists(connection, "ix_relation_project_id"):
121
+ op.create_index("ix_relation_project_id", "relation", ["project_id"])
84
122
 
85
123
  # -------------------------------------------------------------------------
86
124
  # Add project_id to observation table
87
125
  # -------------------------------------------------------------------------
88
126
 
89
- # Step 1: Add project_id column as nullable first
90
- op.add_column("observation", sa.Column("project_id", sa.Integer(), nullable=True))
127
+ # Step 1: Add project_id column as nullable first (idempotent)
128
+ if not column_exists(connection, "observation", "project_id"):
129
+ op.add_column("observation", sa.Column("project_id", sa.Integer(), nullable=True))
91
130
 
92
- # Step 2: Backfill project_id from entity.project_id via entity_id
93
- if dialect == "postgresql":
94
- op.execute("""
95
- UPDATE observation
96
- SET project_id = entity.project_id
97
- FROM entity
98
- WHERE observation.entity_id = entity.id
99
- """)
100
- else:
101
- # SQLite syntax
102
- op.execute("""
103
- UPDATE observation
104
- SET project_id = (
105
- SELECT entity.project_id
131
+ # Step 2: Backfill project_id from entity.project_id via entity_id
132
+ if dialect == "postgresql":
133
+ op.execute("""
134
+ UPDATE observation
135
+ SET project_id = entity.project_id
106
136
  FROM entity
107
- WHERE entity.id = observation.entity_id
108
- )
109
- """)
110
-
111
- # Step 3: Make project_id NOT NULL and add foreign key
112
- if dialect == "postgresql":
113
- op.alter_column("observation", "project_id", nullable=False)
114
- op.create_foreign_key(
115
- "fk_observation_project_id",
116
- "observation",
117
- "project",
118
- ["project_id"],
119
- ["id"],
120
- )
121
- else:
122
- # SQLite requires batch operations for ALTER COLUMN
123
- with op.batch_alter_table("observation") as batch_op:
124
- batch_op.alter_column("project_id", nullable=False)
125
- batch_op.create_foreign_key(
137
+ WHERE observation.entity_id = entity.id
138
+ """)
139
+ else:
140
+ # SQLite syntax
141
+ op.execute("""
142
+ UPDATE observation
143
+ SET project_id = (
144
+ SELECT entity.project_id
145
+ FROM entity
146
+ WHERE entity.id = observation.entity_id
147
+ )
148
+ """)
149
+
150
+ # Step 3: Make project_id NOT NULL and add foreign key
151
+ if dialect == "postgresql":
152
+ op.alter_column("observation", "project_id", nullable=False)
153
+ op.create_foreign_key(
126
154
  "fk_observation_project_id",
155
+ "observation",
127
156
  "project",
128
157
  ["project_id"],
129
158
  ["id"],
130
159
  )
131
-
132
- # Step 4: Create index on observation.project_id
133
- op.create_index("ix_observation_project_id", "observation", ["project_id"])
160
+ else:
161
+ # SQLite requires batch operations for ALTER COLUMN
162
+ with op.batch_alter_table("observation") as batch_op:
163
+ batch_op.alter_column("project_id", nullable=False)
164
+ batch_op.create_foreign_key(
165
+ "fk_observation_project_id",
166
+ "project",
167
+ ["project_id"],
168
+ ["id"],
169
+ )
170
+
171
+ # Step 4: Create index on observation.project_id (idempotent)
172
+ if not index_exists(connection, "ix_observation_project_id"):
173
+ op.create_index("ix_observation_project_id", "observation", ["project_id"])
134
174
 
135
175
  # Postgres-specific: pg_trgm and GIN indexes
136
176
  if dialect == "postgresql":
@@ -53,12 +53,20 @@ async def lifespan(app: FastAPI): # pragma: no cover
53
53
  app.state.session_maker = session_maker
54
54
  logger.info("Database connections cached in app state")
55
55
 
56
- logger.info(f"Sync changes enabled: {app_config.sync_changes}")
57
- if app_config.sync_changes:
56
+ # Start file sync if enabled
57
+ if app_config.sync_changes and not app_config.is_test_env:
58
+ logger.info(f"Sync changes enabled: {app_config.sync_changes}")
59
+
58
60
  # start file sync task in background
59
- app.state.sync_task = asyncio.create_task(initialize_file_sync(app_config))
61
+ async def _file_sync_runner() -> None:
62
+ await initialize_file_sync(app_config)
63
+
64
+ app.state.sync_task = asyncio.create_task(_file_sync_runner())
60
65
  else:
61
- logger.info("Sync changes disabled. Skipping file sync service.")
66
+ if app_config.is_test_env:
67
+ logger.info("Test environment detected. Skipping file sync service.")
68
+ else:
69
+ logger.info("Sync changes disabled. Skipping file sync service.")
62
70
  app.state.sync_task = None
63
71
 
64
72
  # proceed with startup
@@ -274,7 +274,7 @@ async def add_project(
274
274
  raise HTTPException(status_code=500, detail="Failed to retrieve newly created project")
275
275
 
276
276
  return ProjectStatusResponse( # pyright: ignore [reportCallIssue]
277
- message=f"Project '{project_data.name}' added successfully",
277
+ message=f"Project '{new_project.name}' added successfully",
278
278
  status="success",
279
279
  default=project_data.set_default,
280
280
  new_project=ProjectItem(
@@ -329,7 +329,7 @@ async def remove_project(
329
329
  await project_service.remove_project(name, delete_notes=delete_notes)
330
330
 
331
331
  return ProjectStatusResponse(
332
- message=f"Project '{name}' removed successfully",
332
+ message=f"Project '{old_project.name}' removed successfully",
333
333
  status="success",
334
334
  default=False,
335
335
  old_project=ProjectItem(