flurryx-code-memory 0.4.0__tar.gz → 0.5.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 (169) hide show
  1. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/PKG-INFO +1 -1
  2. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/README.md +74 -5
  3. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/pyproject.toml +1 -1
  4. flurryx_code_memory-0.5.1/src/code_memory/__init__.py +16 -0
  5. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/cli.py +55 -0
  6. flurryx_code_memory-0.5.1/src/code_memory/updater.py +453 -0
  7. flurryx_code_memory-0.4.0/src/code_memory/__init__.py +0 -1
  8. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/.claude-plugin/marketplace.json +0 -0
  9. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/.env.example +0 -0
  10. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/.gitignore +0 -0
  11. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/CHANGELOG.md +0 -0
  12. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/docker/docker-compose.yml +0 -0
  13. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/docs/BENCHMARK.md +0 -0
  14. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/docs/BENCHMARK_VS_BASELINE.json +0 -0
  15. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/docs/BENCHMARK_VS_BASELINE.md +0 -0
  16. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/docs/architecture.png +0 -0
  17. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/docs/benchmark-raw.json +0 -0
  18. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/docs/hero.png +0 -0
  19. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/install.ps1 +0 -0
  20. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/install.sh +0 -0
  21. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
  22. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/README.md +0 -0
  23. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/commands/code-memory.md +0 -0
  24. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/hooks/hooks.json +0 -0
  25. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/install.sh +0 -0
  26. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/lib/claim-intent.js +0 -0
  27. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/lib/claim-intent.test.js +0 -0
  28. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/lib/format.js +0 -0
  29. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/lib/intent.js +0 -0
  30. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/lib/io.js +0 -0
  31. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/lib/memory.js +0 -0
  32. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/lib/state.js +0 -0
  33. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/on-post-tool.js +0 -0
  34. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/on-pre-tool.js +0 -0
  35. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/on-retrieve-seen.js +0 -0
  36. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/on-session-start.js +0 -0
  37. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/on-stop.js +0 -0
  38. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/on-user-prompt.js +0 -0
  39. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/scripts/resolver-debounce.js +0 -0
  40. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/claude-code/skills/code-memory/SKILL.md +0 -0
  41. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/README.md +0 -0
  42. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/install.sh +0 -0
  43. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/package-lock.json +0 -0
  44. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/package.json +0 -0
  45. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/scripts/add-mcp.py +0 -0
  46. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/scripts/install.mjs +0 -0
  47. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/scripts/uninstall.mjs +0 -0
  48. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/skills/code-memory/SKILL.md +0 -0
  49. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/src/code-memory-lib/claim-intent.test.mts +0 -0
  50. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/src/code-memory-lib/claim-intent.ts +0 -0
  51. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/src/code-memory-lib/intent.ts +0 -0
  52. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/src/code-memory-lib/memory-client.ts +0 -0
  53. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/src/code-memory.ts +0 -0
  54. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/plugins/opencode/tsconfig.json +0 -0
  55. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/scripts/benchmark.py +0 -0
  56. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/scripts/benchmark_queries.json +0 -0
  57. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/scripts/benchmark_vs_baseline.py +0 -0
  58. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/scripts/benchmark_vs_grep.sh +0 -0
  59. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/scripts/ingest.py +0 -0
  60. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/scripts/install.ps1 +0 -0
  61. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/scripts/install.sh +0 -0
  62. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/claims/__init__.py +0 -0
  63. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/claims/extractor.py +0 -0
  64. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/claims/indexer.py +0 -0
  65. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/claims/resolver.py +0 -0
  66. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/claims/store.py +0 -0
  67. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/config.py +0 -0
  68. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/embed/__init__.py +0 -0
  69. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/embed/cache.py +0 -0
  70. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/embed/m3.py +0 -0
  71. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/embed/ollama.py +0 -0
  72. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/embed/tei.py +0 -0
  73. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/episodic/__init__.py +0 -0
  74. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/episodic/sqlite_store.py +0 -0
  75. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/__init__.py +0 -0
  76. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/csproj.py +0 -0
  77. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/dll.py +0 -0
  78. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/gitignore.py +0 -0
  79. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/nuget.py +0 -0
  80. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/sanity.py +0 -0
  81. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/sln.py +0 -0
  82. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/extractor/treesitter.py +0 -0
  83. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/graph/__init__.py +0 -0
  84. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/graph/falkor_store.py +0 -0
  85. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/mcp_server.py +0 -0
  86. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/metrics.py +0 -0
  87. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/orchestrator/__init__.py +0 -0
  88. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/orchestrator/git_delta.py +0 -0
  89. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/orchestrator/ingest_state.py +0 -0
  90. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/orchestrator/pipeline.py +0 -0
  91. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/orchestrator/reset.py +0 -0
  92. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/orchestrator/resolver.py +0 -0
  93. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/orchestrator/retrieve.py +0 -0
  94. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/resilience.py +0 -0
  95. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/__init__.py +0 -0
  96. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/autostart/__init__.py +0 -0
  97. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/autostart/base.py +0 -0
  98. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/autostart/launchd.py +0 -0
  99. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/autostart/schtasks.py +0 -0
  100. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/autostart/systemd.py +0 -0
  101. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/hooks.py +0 -0
  102. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/safety.py +0 -0
  103. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/snapshot.py +0 -0
  104. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/store.py +0 -0
  105. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/sync.py +0 -0
  106. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/sync/watcher.py +0 -0
  107. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/vector/__init__.py +0 -0
  108. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/src/code_memory/vector/qdrant_store.py +0 -0
  109. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_autostart_adapters.py +0 -0
  110. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_chunk_text.py +0 -0
  111. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_claim_extractor.py +0 -0
  112. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_claim_indexer.py +0 -0
  113. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_claim_resolver.py +0 -0
  114. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_claim_store.py +0 -0
  115. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_config_embed_dim.py +0 -0
  116. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_config_sentinel.py +0 -0
  117. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_csproj.py +0 -0
  118. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_dll_members.py +0 -0
  119. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_dll_parser.py +0 -0
  120. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_embed_backend.py +0 -0
  121. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_embed_cache.py +0 -0
  122. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_embed_m3.py +0 -0
  123. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_embed_tei.py +0 -0
  124. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_episode_dedup.py +0 -0
  125. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_episode_head_sha.py +0 -0
  126. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_csharp.py +0 -0
  127. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_filters.py +0 -0
  128. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_php.py +0 -0
  129. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_python_imports.py +0 -0
  130. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_receiver_type.py +0 -0
  131. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_references.py +0 -0
  132. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_sanity.py +0 -0
  133. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_ts_abstract.py +0 -0
  134. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_ts_inject.py +0 -0
  135. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_extractor_utf8.py +0 -0
  136. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_file_containment.py +0 -0
  137. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_git_delta.py +0 -0
  138. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_graph_queries.py +0 -0
  139. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_graph_temporal.py +0 -0
  140. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_graph_vacuum_at_sha.py +0 -0
  141. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_hooks_installer.py +0 -0
  142. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_ingest_state.py +0 -0
  143. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_mcp_assert_claim.py +0 -0
  144. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_mcp_server_descriptions.py +0 -0
  145. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_mcp_strict_project.py +0 -0
  146. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_metrics.py +0 -0
  147. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_nuget_resolver.py +0 -0
  148. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_overload_resolution.py +0 -0
  149. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_partial_class.py +0 -0
  150. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_pipeline_references.py +0 -0
  151. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_pipeline_temporal_wiring.py +0 -0
  152. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_qdrant_legacy_guard.py +0 -0
  153. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_razor_inject.py +0 -0
  154. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_resilience.py +0 -0
  155. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_resolver.py +0 -0
  156. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_resolver_assembly.py +0 -0
  157. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_retrieve_claims_surfacing.py +0 -0
  158. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_retrieve_rerank.py +0 -0
  159. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_sln.py +0 -0
  160. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_smoke.py +0 -0
  161. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_snapshot_e2e.py +0 -0
  162. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_snapshot_format.py +0 -0
  163. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_snapshot_store.py +0 -0
  164. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_sync_decision_tree.py +0 -0
  165. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_watch_safety.py +0 -0
  166. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_watcher_debouncer.py +0 -0
  167. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_watcher_exclude.py +0 -0
  168. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/tests/test_watcher_ref_events.py +0 -0
  169. {flurryx_code_memory-0.4.0 → flurryx_code_memory-0.5.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flurryx-code-memory
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  Summary: Local lightweight memory layer for coding agents: FalkorDB + Qdrant + Ollama (BGE-M3) + tree-sitter
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: anyio>=4.4
@@ -17,7 +17,7 @@ Structural symbol graph  ·  semantic vector recall  ·  epi
17
17
  [![tree-sitter](https://img.shields.io/badge/parser-tree--sitter-228B22)](https://tree-sitter.github.io/)
18
18
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
19
19
 
20
- **Jump to:** [Get it running](#installation)  ·  [Plug it into your agent](#mcp-server)  ·  [How it scores vs `rg`](#benchmarks)
20
+ **Jump to:** [Get it running](#installation)  ·  [Update it](#updating)  ·  [Plug it into your agent](#mcp-server)  ·  [How it scores vs `rg`](#benchmarks)
21
21
 
22
22
  </div>
23
23
 
@@ -66,6 +66,22 @@ _Full tables + methodology in [Benchmarks](#benchmarks)._
66
66
 
67
67
  ## Installation
68
68
 
69
+ > [!TIP]
70
+ > ### 🔄 Already installed? Update in one command.
71
+ >
72
+ > ```bash
73
+ > code-memory update # smart refresh: CLI + Docker images + present Ollama models + plugins
74
+ > code-memory update --check # dry-run: print current vs latest, exit non-zero if behind
75
+ > code-memory update --full # re-run the one-liner installer (adds anything missing)
76
+ > ```
77
+ >
78
+ > `update` is **idempotent and selective** — it only touches components that are already
79
+ > installed locally. No re-prompting for extras you opted out of, no docker churn if
80
+ > compose isn't on this machine. Run it weekly, or after a release announcement.
81
+ >
82
+ > Bleeding edge from `main`: `code-memory update --bleeding`. Full details below in
83
+ > [Updating](#updating).
84
+
69
85
  `code-memory` installs in **one command**. Pick your OS, paste it, you're done.
70
86
 
71
87
  **🍎 macOS**
@@ -154,8 +170,13 @@ iwr https://raw.githubusercontent.com/fmflurry/code-memory/main/install.ps1 -Out
154
170
  <summary><strong>Manual install (à la carte, no one-liner)</strong></summary>
155
171
 
156
172
  ```bash
157
- # 1. CLI (via uv)
158
- uv tool install --from git+https://github.com/fmflurry/code-memory code-memory
173
+ # 1. CLI (PyPI — recommended)
174
+ uv tool install flurryx-code-memory
175
+ # or: pipx install flurryx-code-memory
176
+ # or: pip install flurryx-code-memory
177
+ #
178
+ # Bleeding edge from main:
179
+ # uv tool install --from git+https://github.com/fmflurry/code-memory flurryx-code-memory
159
180
 
160
181
  # 2. Infra (compose + env)
161
182
  mkdir -p ~/.code-memory/docker
@@ -173,7 +194,7 @@ claude plugin marketplace add https://github.com/fmflurry/code-memory
173
194
  claude plugin install code-memory@code-memory --scope user
174
195
  claude mcp add code-memory --scope user \
175
196
  -e CODE_MEMORY_PROJECT=auto \
176
- -- uvx --from git+https://github.com/fmflurry/code-memory code-memory-mcp
197
+ -- uvx --from flurryx-code-memory code-memory-mcp
177
198
 
178
199
  # 5. OpenCode plugin
179
200
  npm i -g code-memory-opencode
@@ -189,7 +210,7 @@ Just the Claude Code MCP server — no auto-retrieve / auto-record hooks, no Doc
189
210
 
190
211
  ```bash
191
212
  claude mcp add code-memory \
192
- -- uvx --from git+https://github.com/fmflurry/code-memory code-memory-mcp
213
+ -- uvx --from flurryx-code-memory code-memory-mcp
193
214
  ```
194
215
 
195
216
  </details>
@@ -238,6 +259,54 @@ cd code-memory
238
259
 
239
260
  ---
240
261
 
262
+ ## Updating
263
+
264
+ After the initial install, never run the one-liner again — use the built-in updater:
265
+
266
+ ```bash
267
+ code-memory update # smart, idempotent, no re-prompts
268
+ code-memory update --check # dry-run; exit 1 if behind, 0 if current
269
+ code-memory update --full # behave like a fresh one-liner install
270
+ code-memory update --bleeding # install CLI from git+main instead of PyPI
271
+ ```
272
+
273
+ ### What it does
274
+
275
+ `update` introspects your machine and refreshes **only what is already installed**:
276
+
277
+ | Component | Detection | Refresh action |
278
+ | --------------------------- | ------------------------------------------------------ | ------------------------------------------------- |
279
+ | **CLI** | `sys.prefix` (uv tool / pipx / pip / editable) | upgrade via the same channel |
280
+ | **FalkorDB + Qdrant** | `~/.code-memory/docker/docker-compose.yml` or running | `docker compose pull && up -d` |
281
+ | **Ollama models** | present in `ollama list` (`bge-m3`, `gemma2:9b`, …) | `ollama pull <model>` (only for already-pulled) |
282
+ | **Claude Code plugin** | `claude plugin list \| grep code-memory` | `claude plugin install … --force` |
283
+ | **OpenCode plugin** | `npm ls -g code-memory-opencode` | `npm i -g code-memory-opencode` |
284
+ | **Python extras** | `FlagEmbedding` / `dnfile` import probe | covered by the CLI upgrade |
285
+
286
+ Anything you **didn't** install stays untouched. No "do you want gemma?" prompt, no
287
+ docker churn on a machine that hits remote infra, no plugin re-registration if you
288
+ deliberately removed it.
289
+
290
+ ### Sample output
291
+
292
+ ```
293
+ $ code-memory update --check
294
+ code-memory updater (install: uv-tool)
295
+ CLI: 0.4.0 → latest: 0.5.0
296
+ Components detected locally:
297
+ • Docker: FalkorDB (running)
298
+ • Docker: Qdrant (running)
299
+ • Ollama: bge-m3
300
+ · Ollama: gemma2:9b [not installed — skip]
301
+ • Claude Code plugin
302
+ • Claude Code MCP
303
+ · OpenCode plugin (npm) [not installed — skip]
304
+ ```
305
+
306
+ Use `code-memory update --check` from CI or a cron to nudge devs when a release ships.
307
+
308
+ ---
309
+
241
310
  ## What is this?
242
311
 
243
312
  `code-memory` gives a coding agent (Claude Code, OpenCode, Cursor, your own harness) a memory it can actually use:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "flurryx-code-memory"
7
- version = "0.4.0"
7
+ version = "0.5.1"
8
8
  description = "Local lightweight memory layer for coding agents: FalkorDB + Qdrant + Ollama (BGE-M3) + tree-sitter"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = [
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib.metadata import PackageNotFoundError, version as _pkg_version
4
+
5
+ # Two dist names exist in the wild:
6
+ # * "flurryx-code-memory" — current PyPI name (>= 0.4.0)
7
+ # * "code-memory" — legacy name used by uv-tool installs from git+main
8
+ # before the rename. Falls back to keep the older installs reporting
9
+ # a real version until they run `code-memory update --bleeding`.
10
+ __version__ = "0.0.0+local"
11
+ for _name in ("flurryx-code-memory", "code-memory"):
12
+ try:
13
+ __version__ = _pkg_version(_name)
14
+ break
15
+ except PackageNotFoundError:
16
+ continue
@@ -27,6 +27,28 @@ def _graph_for(project: str | None) -> FalkorStore:
27
27
  app = typer.Typer(no_args_is_help=True, add_completion=False, help="code-memory CLI")
28
28
 
29
29
 
30
+ def _version_callback(value: bool) -> None:
31
+ if value:
32
+ from . import __version__
33
+
34
+ typer.echo(__version__)
35
+ raise typer.Exit()
36
+
37
+
38
+ @app.callback()
39
+ def _main(
40
+ version: bool = typer.Option(
41
+ False,
42
+ "--version",
43
+ "-V",
44
+ callback=_version_callback,
45
+ is_eager=True,
46
+ help="Print version and exit.",
47
+ ),
48
+ ) -> None:
49
+ """code-memory CLI."""
50
+
51
+
30
52
  ProjectOpt = typer.Option(
31
53
  None,
32
54
  "--project",
@@ -1188,5 +1210,38 @@ def autostart_status(
1188
1210
  )
1189
1211
 
1190
1212
 
1213
+ @app.command()
1214
+ def update(
1215
+ check: bool = typer.Option(
1216
+ False,
1217
+ "--check",
1218
+ help="Only print the current/latest state and exit (no changes).",
1219
+ ),
1220
+ full: bool = typer.Option(
1221
+ False,
1222
+ "--full",
1223
+ help="Re-run the one-liner installer (refresh docker/env/plugins from scratch).",
1224
+ ),
1225
+ bleeding: bool = typer.Option(
1226
+ False,
1227
+ "--bleeding",
1228
+ help="Install CLI from git+main instead of PyPI.",
1229
+ ),
1230
+ ) -> None:
1231
+ """Smart-update code-memory: refresh only components already installed locally.
1232
+
1233
+ Default behavior detects the CLI install method (uv tool / pipx / pip) and
1234
+ upgrades it in place, then refreshes Docker images, present Ollama models,
1235
+ and registered Claude/OpenCode plugins. Pieces that were never installed
1236
+ stay untouched — no prompts, no re-asking.
1237
+
1238
+ Use ``--check`` for a dry-run, ``--full`` to behave like a fresh install.
1239
+ """
1240
+ from .updater import run_update
1241
+
1242
+ rc = run_update(check_only=check, full=full, bleeding=bleeding)
1243
+ raise typer.Exit(code=rc)
1244
+
1245
+
1191
1246
  if __name__ == "__main__":
1192
1247
  app()
@@ -0,0 +1,453 @@
1
+ """Smart updater for code-memory.
2
+
3
+ Detects which components are already installed locally and refreshes
4
+ only those — never asks the user to re-confirm pieces they already opted
5
+ into during the initial one-liner install.
6
+
7
+ Components inspected:
8
+
9
+ * CLI install method (``uv tool`` / ``pipx`` / ``pip``) + version vs PyPI
10
+ * Docker stack (FalkorDB + Qdrant)
11
+ * Ollama models present locally (``bge-m3``, optionally ``gemma2:9b``)
12
+ * Optional Python extras (``hybrid`` via ``FlagEmbedding``, ``dotnet`` via ``dnfile``)
13
+ * Claude Code plugin + MCP server registration
14
+ * OpenCode global npm package
15
+
16
+ The update flow is intentionally idempotent and noisy-on-change only:
17
+ already-current items print one line each, anything actually upgraded
18
+ prints its old/new state.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import importlib.util
24
+ import json
25
+ import os
26
+ import shutil
27
+ import subprocess
28
+ import sys
29
+ from dataclasses import dataclass, field
30
+ from pathlib import Path
31
+ from typing import Literal
32
+
33
+ import httpx
34
+
35
+ from . import __version__ as _LOCAL_VERSION
36
+
37
+ PYPI_PACKAGE = "flurryx-code-memory"
38
+ LEGACY_PACKAGE = "code-memory" # historical dist name still in older uv-tool venvs
39
+ UV_TOOL_NAME = "code-memory" # entry-point/tool-name; what `uv tool list` shows
40
+ DEFAULT_REPO_URL = os.environ.get(
41
+ "CODEMEMORY_REPO_URL", "https://github.com/fmflurry/code-memory"
42
+ )
43
+ CODEMEMORY_HOME = Path(os.environ.get("CODEMEMORY_HOME", str(Path.home() / ".code-memory")))
44
+
45
+ InstallMethod = Literal["uv-tool", "pipx", "pip", "editable", "unknown"]
46
+
47
+
48
+ @dataclass
49
+ class ComponentState:
50
+ name: str
51
+ present: bool
52
+ detail: str = ""
53
+ current: str | None = None
54
+ latest: str | None = None
55
+
56
+
57
+ @dataclass
58
+ class UpdatePlan:
59
+ install_method: InstallMethod
60
+ components: list[ComponentState] = field(default_factory=list)
61
+ cli_current: str = ""
62
+ cli_latest: str = ""
63
+
64
+
65
+ # ---------- detection ----------
66
+
67
+
68
+ def _run(cmd: list[str], *, check: bool = False, capture: bool = True) -> subprocess.CompletedProcess[str]:
69
+ return subprocess.run(
70
+ cmd, check=check, capture_output=capture, text=True, env={**os.environ}
71
+ )
72
+
73
+
74
+ def _have(binary: str) -> bool:
75
+ return shutil.which(binary) is not None
76
+
77
+
78
+ def current_cli_version() -> str:
79
+ return _LOCAL_VERSION
80
+
81
+
82
+ def latest_pypi_version(pkg: str = PYPI_PACKAGE, *, timeout: float = 5.0) -> str | None:
83
+ try:
84
+ r = httpx.get(f"https://pypi.org/pypi/{pkg}/json", timeout=timeout)
85
+ r.raise_for_status()
86
+ return r.json()["info"]["version"]
87
+ except Exception: # noqa: BLE001
88
+ return None
89
+
90
+
91
+ def detect_install_method() -> InstallMethod:
92
+ """Best-effort: where is the running interpreter living?
93
+
94
+ Editable installs are detected via PEP 610 ``direct_url.json``;
95
+ uv tool / pipx are detected from ``sys.prefix`` (the venv root),
96
+ not ``sys.executable`` which on macOS often resolves through a
97
+ symlink into Homebrew/conda.
98
+ """
99
+ if _is_editable_install():
100
+ return "editable"
101
+
102
+ prefix = str(Path(sys.prefix).resolve()).lower()
103
+ if "/uv/tools/" in prefix or "\\uv\\tools\\" in prefix:
104
+ return "uv-tool"
105
+ if "/pipx/venvs/" in prefix or "\\pipx\\venvs\\" in prefix:
106
+ return "pipx"
107
+ if _have("pip") and _pip_shows():
108
+ return "pip"
109
+ return "unknown"
110
+
111
+
112
+ def _is_editable_install() -> bool:
113
+ from importlib.metadata import distribution
114
+
115
+ for name in (PYPI_PACKAGE, LEGACY_PACKAGE):
116
+ try:
117
+ d = distribution(name)
118
+ except Exception: # noqa: BLE001
119
+ continue
120
+ try:
121
+ raw = d.read_text("direct_url.json") or ""
122
+ except Exception: # noqa: BLE001
123
+ raw = ""
124
+ if not raw:
125
+ continue
126
+ if bool(json.loads(raw).get("dir_info", {}).get("editable")):
127
+ return True
128
+ return False
129
+
130
+
131
+ def _pip_shows() -> bool:
132
+ for name in (PYPI_PACKAGE, LEGACY_PACKAGE):
133
+ if _run([sys.executable, "-m", "pip", "show", name]).returncode == 0:
134
+ return True
135
+ return False
136
+
137
+
138
+ def _ollama_models() -> list[str]:
139
+ if not _have("ollama"):
140
+ return []
141
+ p = _run(["ollama", "list"])
142
+ if p.returncode != 0:
143
+ return []
144
+ names: list[str] = []
145
+ for line in p.stdout.splitlines()[1:]:
146
+ first = line.split()
147
+ if first:
148
+ names.append(first[0])
149
+ return names
150
+
151
+
152
+ def _docker_compose_present() -> bool:
153
+ return (CODEMEMORY_HOME / "docker" / "docker-compose.yml").exists()
154
+
155
+
156
+ def _docker_running(service: str) -> bool:
157
+ if not _have("docker"):
158
+ return False
159
+ p = _run(["docker", "ps", "--format", "{{.Names}}"])
160
+ if p.returncode != 0:
161
+ return False
162
+ return any(service in name for name in p.stdout.splitlines())
163
+
164
+
165
+ def _running_compose_file() -> Path | None:
166
+ """Probe live containers for the compose file that owns them.
167
+
168
+ Handles dev installs whose compose file lives in the repo, not under
169
+ ``~/.code-memory/docker/``. Returns the first compose path found
170
+ among ``cm-falkordb`` / ``cm-qdrant`` containers, or None.
171
+ """
172
+ if not _have("docker"):
173
+ return None
174
+ for name in ("cm-falkordb", "cm-qdrant"):
175
+ p = _run([
176
+ "docker",
177
+ "inspect",
178
+ "-f",
179
+ "{{ index .Config.Labels \"com.docker.compose.project.config_files\" }}",
180
+ name,
181
+ ])
182
+ if p.returncode == 0:
183
+ path = p.stdout.strip()
184
+ if path and Path(path).exists():
185
+ return Path(path)
186
+ return None
187
+
188
+
189
+ def _claude_plugin_present() -> bool:
190
+ if not _have("claude"):
191
+ return False
192
+ p = _run(["claude", "plugin", "list"])
193
+ return p.returncode == 0 and "code-memory" in p.stdout
194
+
195
+
196
+ def _claude_mcp_present() -> bool:
197
+ if not _have("claude"):
198
+ return False
199
+ p = _run(["claude", "mcp", "list"])
200
+ return p.returncode == 0 and "code-memory" in p.stdout
201
+
202
+
203
+ def _npm_pkg_present(pkg: str = "code-memory-opencode") -> bool:
204
+ if not _have("npm"):
205
+ return False
206
+ p = _run(["npm", "ls", "-g", "--depth=0", "--json"])
207
+ if p.returncode != 0:
208
+ return False
209
+ try:
210
+ deps = json.loads(p.stdout).get("dependencies", {})
211
+ return pkg in deps
212
+ except Exception: # noqa: BLE001
213
+ return False
214
+
215
+
216
+ def _python_module_present(mod: str) -> bool:
217
+ return importlib.util.find_spec(mod) is not None
218
+
219
+
220
+ def build_plan() -> UpdatePlan:
221
+ plan = UpdatePlan(install_method=detect_install_method())
222
+ plan.cli_current = current_cli_version()
223
+ plan.cli_latest = latest_pypi_version() or "?"
224
+
225
+ falkor_running = _docker_running("falkor")
226
+ qdrant_running = _docker_running("qdrant")
227
+ compose_here = _docker_compose_present()
228
+
229
+ plan.components = [
230
+ ComponentState(
231
+ name="CLI (flurryx-code-memory)",
232
+ present=True,
233
+ detail=f"via {plan.install_method}",
234
+ current=plan.cli_current,
235
+ latest=plan.cli_latest,
236
+ ),
237
+ ComponentState(
238
+ name="Docker: FalkorDB",
239
+ present=compose_here or falkor_running,
240
+ detail="running" if falkor_running else ("compose present" if compose_here else "stopped"),
241
+ ),
242
+ ComponentState(
243
+ name="Docker: Qdrant",
244
+ present=compose_here or qdrant_running,
245
+ detail="running" if qdrant_running else ("compose present" if compose_here else "stopped"),
246
+ ),
247
+ ]
248
+
249
+ models = _ollama_models()
250
+ for m in ("bge-m3", "gemma2:9b"):
251
+ plan.components.append(
252
+ ComponentState(
253
+ name=f"Ollama: {m}",
254
+ present=any(name.startswith(m) for name in models),
255
+ )
256
+ )
257
+
258
+ plan.components.append(
259
+ ComponentState(
260
+ name="Extra: hybrid (FlagEmbedding)",
261
+ present=_python_module_present("FlagEmbedding"),
262
+ )
263
+ )
264
+ plan.components.append(
265
+ ComponentState(
266
+ name="Extra: dotnet (dnfile)",
267
+ present=_python_module_present("dnfile"),
268
+ )
269
+ )
270
+
271
+ plan.components.append(
272
+ ComponentState(name="Claude Code plugin", present=_claude_plugin_present())
273
+ )
274
+ plan.components.append(
275
+ ComponentState(name="Claude Code MCP", present=_claude_mcp_present())
276
+ )
277
+ plan.components.append(
278
+ ComponentState(name="OpenCode plugin (npm)", present=_npm_pkg_present())
279
+ )
280
+
281
+ return plan
282
+
283
+
284
+ # ---------- upgrade actions ----------
285
+
286
+
287
+ def upgrade_cli(method: InstallMethod, *, bleeding: bool = False) -> tuple[bool, str]:
288
+ """Upgrade the CLI in-place via the same channel it was installed from.
289
+
290
+ For uv-tool we always do a ``--reinstall --from <source>`` so legacy
291
+ installs whose dist is named ``code-memory`` (pre-rename) get cleanly
292
+ migrated to ``flurryx-code-memory`` without the user noticing.
293
+ """
294
+ source = f"git+{DEFAULT_REPO_URL}" if bleeding else PYPI_PACKAGE
295
+ if method == "uv-tool":
296
+ cmd = [
297
+ "uv",
298
+ "tool",
299
+ "install",
300
+ "--reinstall",
301
+ "--force",
302
+ "--from",
303
+ source,
304
+ UV_TOOL_NAME,
305
+ ]
306
+ elif method == "pipx":
307
+ # pipx-installed users likely registered under either name; force
308
+ # a reinstall from <source> so the package name converges to current.
309
+ cmd = ["pipx", "install", "--force", source]
310
+ elif method == "pip":
311
+ cmd = [sys.executable, "-m", "pip", "install", "--upgrade", source]
312
+ elif method == "editable":
313
+ return False, "editable install — `git pull` in the repo to update"
314
+ else:
315
+ return False, "unknown install method — re-run the one-liner installer"
316
+ p = _run(cmd, capture=False)
317
+ return p.returncode == 0, " ".join(cmd)
318
+
319
+
320
+ def upgrade_docker_images() -> tuple[bool, str]:
321
+ if not _have("docker"):
322
+ return False, "docker not on PATH"
323
+ compose = CODEMEMORY_HOME / "docker" / "docker-compose.yml"
324
+ project_dir = CODEMEMORY_HOME
325
+ if not compose.exists():
326
+ live = _running_compose_file()
327
+ if live is None:
328
+ return False, "no compose file at ~/.code-memory/ and no running cm-* containers"
329
+ compose = live
330
+ project_dir = live.parent.parent if live.parent.name == "docker" else live.parent
331
+ pull = _run(
332
+ ["docker", "compose", "-f", str(compose), "--project-directory", str(project_dir), "pull"],
333
+ capture=False,
334
+ )
335
+ if pull.returncode != 0:
336
+ return False, "docker compose pull failed"
337
+ up = _run(
338
+ ["docker", "compose", "-f", str(compose), "--project-directory", str(project_dir), "up", "-d"],
339
+ capture=False,
340
+ )
341
+ return up.returncode == 0, f"compose pulled + up ({compose})"
342
+
343
+
344
+ def upgrade_ollama_model(model: str) -> tuple[bool, str]:
345
+ if not _have("ollama"):
346
+ return False, "ollama not on PATH"
347
+ p = _run(["ollama", "pull", model], capture=False)
348
+ return p.returncode == 0, f"pulled {model}"
349
+
350
+
351
+ def upgrade_claude_plugin() -> tuple[bool, str]:
352
+ if not _have("claude"):
353
+ return False, "claude CLI not on PATH"
354
+ # `claude plugin update <name>` is the canonical refresh path.
355
+ # `--force` was a previous-version flag; current CLI rejects it.
356
+ p = _run(["claude", "plugin", "update", "code-memory@code-memory"], capture=False)
357
+ if p.returncode == 0:
358
+ return True, "claude plugin updated"
359
+ # Fall back to install — handles the never-installed-after-marketplace-add edge.
360
+ p2 = _run(
361
+ ["claude", "plugin", "install", "code-memory@code-memory", "--scope", "user"],
362
+ capture=False,
363
+ )
364
+ return p2.returncode == 0, "claude plugin re-installed"
365
+
366
+
367
+ def upgrade_npm_pkg(pkg: str = "code-memory-opencode") -> tuple[bool, str]:
368
+ if not _have("npm"):
369
+ return False, "npm not on PATH"
370
+ p = _run(["npm", "i", "-g", pkg], capture=False)
371
+ return p.returncode == 0, f"npm i -g {pkg}"
372
+
373
+
374
+ # ---------- orchestrator ----------
375
+
376
+
377
+ def run_update(*, check_only: bool, full: bool, bleeding: bool) -> int:
378
+ """Top-level entry point used by the CLI.
379
+
380
+ ``check_only`` prints the plan and exits 0/1 based on whether anything
381
+ is behind. ``full`` re-runs the one-liner installer (curl … | bash).
382
+ Default is the smart path: upgrade-in-place only what is already present.
383
+ """
384
+ plan = build_plan()
385
+ _print_plan(plan)
386
+
387
+ behind_cli = plan.cli_latest not in ("?", plan.cli_current)
388
+
389
+ if check_only:
390
+ return 1 if behind_cli else 0
391
+
392
+ if full:
393
+ return _run_full_installer()
394
+
395
+ rc = 0
396
+ if behind_cli:
397
+ ok, detail = upgrade_cli(plan.install_method, bleeding=bleeding)
398
+ print(f" CLI upgrade: {'ok' if ok else 'FAILED'} — {detail}")
399
+ rc |= 0 if ok else 1
400
+ else:
401
+ print(f" CLI: already {plan.cli_current}")
402
+
403
+ for comp in plan.components:
404
+ if not comp.present:
405
+ continue
406
+ if comp.name == "Docker: FalkorDB" or comp.name == "Docker: Qdrant":
407
+ # one pass for both
408
+ continue
409
+ if comp.name.startswith("Ollama: "):
410
+ model = comp.name.split(": ", 1)[1]
411
+ ok, detail = upgrade_ollama_model(model)
412
+ print(f" {comp.name}: {'ok' if ok else 'skip'} — {detail}")
413
+ elif comp.name == "Claude Code plugin":
414
+ ok, detail = upgrade_claude_plugin()
415
+ print(f" {comp.name}: {'ok' if ok else 'skip'} — {detail}")
416
+ elif comp.name == "OpenCode plugin (npm)":
417
+ ok, detail = upgrade_npm_pkg()
418
+ print(f" {comp.name}: {'ok' if ok else 'skip'} — {detail}")
419
+
420
+ if any(c.present for c in plan.components if c.name.startswith("Docker:")):
421
+ ok, detail = upgrade_docker_images()
422
+ print(f" Docker stack: {'ok' if ok else 'skip'} — {detail}")
423
+
424
+ return rc
425
+
426
+
427
+ def _print_plan(plan: UpdatePlan) -> None:
428
+ print(f"code-memory updater (install: {plan.install_method})")
429
+ print(f" CLI: {plan.cli_current} → latest: {plan.cli_latest}")
430
+ print(" Components detected locally:")
431
+ for c in plan.components[1:]: # skip CLI row, already shown
432
+ mark = "•" if c.present else "·"
433
+ suffix = f" ({c.detail})" if c.detail else ""
434
+ state = "" if c.present else " [not installed — skip]"
435
+ print(f" {mark} {c.name}{suffix}{state}")
436
+
437
+
438
+ def _run_full_installer() -> int:
439
+ """Pipe the one-liner installer through bash (or PowerShell on Windows)."""
440
+ raw = os.environ.get(
441
+ "CODEMEMORY_RAW_URL", "https://raw.githubusercontent.com/fmflurry/code-memory/main"
442
+ )
443
+ if sys.platform == "win32":
444
+ cmd = [
445
+ "powershell",
446
+ "-NoProfile",
447
+ "-Command",
448
+ f"irm {raw}/install.ps1 | iex",
449
+ ]
450
+ else:
451
+ cmd = ["bash", "-c", f"curl -fsSL {raw}/install.sh | bash"]
452
+ p = _run(cmd, capture=False)
453
+ return p.returncode
@@ -1 +0,0 @@
1
- __version__ = "0.1.0"