flurryx-code-memory 0.6.2__tar.gz → 0.7.5__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.
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/CHANGELOG.md +220 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/PKG-INFO +2 -2
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/README.md +7 -2
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/install.ps1 +43 -19
- {flurryx_code_memory-0.6.2/plugins/cursor → flurryx_code_memory-0.7.5/plugins/claude-code}/scripts/lib/memory.js +85 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/on-session-start.js +12 -2
- {flurryx_code_memory-0.6.2/plugins/claude-code → flurryx_code_memory-0.7.5/plugins/cursor}/scripts/lib/memory.js +85 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-session-start.js +10 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/pyproject.toml +2 -2
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/scripts/install.ps1 +44 -20
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/cli.py +50 -8
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/config.py +170 -5
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/embed/ollama.py +18 -1
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/treesitter.py +13 -3
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/graph/falkor_store.py +72 -1
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/mcp_server.py +140 -8
- flurryx_code_memory-0.7.5/src/code_memory/orchestrator/ingest_state.py +117 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/orchestrator/pipeline.py +305 -26
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/autostart/base.py +5 -4
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/autostart/launchd.py +2 -2
- flurryx_code_memory-0.7.5/src/code_memory/sync/safety.py +182 -0
- flurryx_code_memory-0.7.5/src/code_memory/sync/single_flight.py +291 -0
- flurryx_code_memory-0.7.5/tests/test_config_ipv4_defaults.py +117 -0
- flurryx_code_memory-0.7.5/tests/test_embed_ollama_timeout.py +152 -0
- flurryx_code_memory-0.7.5/tests/test_ensure_fresh_no_blocking.py +408 -0
- flurryx_code_memory-0.7.5/tests/test_extractor_parser_compat.py +27 -0
- flurryx_code_memory-0.7.5/tests/test_graph_shadow_swap.py +304 -0
- flurryx_code_memory-0.7.5/tests/test_ingest_safety_and_lock.py +215 -0
- flurryx_code_memory-0.7.5/tests/test_ingest_state.py +131 -0
- flurryx_code_memory-0.7.5/tests/test_pipeline_health_check.py +192 -0
- flurryx_code_memory-0.7.5/tests/test_worktree_autostart_guard.py +293 -0
- flurryx_code_memory-0.7.5/tests/test_worktree_slug.py +225 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/uv.lock +7 -10
- flurryx_code_memory-0.6.2/src/code_memory/orchestrator/ingest_state.py +0 -71
- flurryx_code_memory-0.6.2/src/code_memory/sync/safety.py +0 -93
- flurryx_code_memory-0.6.2/tests/test_ingest_state.py +0 -62
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/.claude-plugin/marketplace.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/.env.example +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/.gitignore +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/docker/docker-compose.yml +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/docs/BENCHMARK.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/docs/BENCHMARK_VS_BASELINE.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/docs/BENCHMARK_VS_BASELINE.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/docs/architecture.png +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/docs/benchmark-raw.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/docs/hero.png +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/install.sh +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/README.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/commands/code-memory.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/hooks/hooks.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/install.sh +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/lib/claim-intent.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/lib/claim-intent.test.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/lib/io.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/lib/state.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/on-post-tool.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/on-pre-tool.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/on-retrieve-seen.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/on-stop.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/on-user-prompt.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/scripts/resolver-debounce.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/claude-code/skills/code-memory/SKILL.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/README.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/hooks/hooks.json.template +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/install.sh +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/rules/code-memory.mdc +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/lib/claim-intent.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/lib/claim-intent.test.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/lib/io.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/lib/state.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-after-file-edit.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-before-mcp-execution.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-before-submit-prompt.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-post-tool-use.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-pre-compact.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-pre-tool-use.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-session-end.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-stop.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/resolver-debounce.js +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/README.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/install.sh +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/package-lock.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/package.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/scripts/add-mcp.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/scripts/install.mjs +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/scripts/uninstall.mjs +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/skills/code-memory/SKILL.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/src/code-memory-lib/claim-intent.test.mts +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/src/code-memory-lib/claim-intent.ts +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/src/code-memory-lib/memory-client.ts +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/src/code-memory.ts +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/opencode/tsconfig.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/vibe/README.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/vibe/install.sh +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/vibe/skills/code-memory/SKILL.md +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/scripts/benchmark.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/scripts/benchmark_queries.json +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/scripts/benchmark_vs_baseline.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/scripts/benchmark_vs_grep.sh +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/scripts/ingest.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/scripts/install.sh +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/claims/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/claims/extractor.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/claims/indexer.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/claims/resolver.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/claims/store.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/embed/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/embed/cache.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/embed/m3.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/embed/tei.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/episodic/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/episodic/sqlite_store.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/csproj.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/dll.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/gitignore.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/nuget.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/sanity.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/extractor/sln.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/graph/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/metrics.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/orchestrator/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/orchestrator/git_delta.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/orchestrator/reset.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/orchestrator/resolver.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/orchestrator/retrieve.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/resilience.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/autostart/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/autostart/schtasks.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/autostart/systemd.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/hooks.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/snapshot.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/store.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/sync.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/sync/watcher.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/updater.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/vector/__init__.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/src/code_memory/vector/qdrant_store.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_autostart_adapters.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_chunk_text.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_claim_extractor.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_claim_indexer.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_claim_resolver.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_claim_store.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_config_embed_dim.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_config_sentinel.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_csproj.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_dll_members.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_dll_parser.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_embed_backend.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_embed_cache.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_embed_m3.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_embed_tei.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_episode_dedup.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_episode_head_sha.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_csharp.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_dart.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_filters.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_php.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_python_imports.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_receiver_type.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_references.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_sanity.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_ts_abstract.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_ts_inject.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_extractor_utf8.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_file_containment.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_git_delta.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_graph_queries.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_graph_temporal.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_graph_vacuum_at_sha.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_hooks_installer.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_mcp_assert_claim.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_mcp_server_descriptions.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_mcp_shutdown.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_mcp_strict_project.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_metrics.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_nuget_resolver.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_overload_resolution.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_partial_class.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_pipeline_references.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_pipeline_temporal_wiring.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_qdrant_legacy_guard.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_razor_inject.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_resilience.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_resolver.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_resolver_assembly.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_retrieve_claims_surfacing.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_retrieve_rerank.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_sln.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_smoke.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_snapshot_e2e.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_snapshot_format.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_snapshot_store.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_sync_decision_tree.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_watch_safety.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_watcher_debouncer.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_watcher_exclude.py +0 -0
- {flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/tests/test_watcher_ref_events.py +0 -0
|
@@ -8,6 +8,226 @@ when the repo grows.
|
|
|
8
8
|
This file complements `git log`: commits explain mechanics, this file
|
|
9
9
|
explains intent.
|
|
10
10
|
|
|
11
|
+
## [0.7.5] — 2026-06-21
|
|
12
|
+
|
|
13
|
+
Release theme: **Windows ingest fix — pin tree-sitter-language-pack**.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
**What:** pinned `tree-sitter-language-pack` to `==1.0.0` (was `>=0.7`).
|
|
18
|
+
Version 1.9.1 (and other releases > 1.0.0) ship a broken Windows wheel:
|
|
19
|
+
`get_language()` returns a `builtins.Language` that core `tree-sitter`
|
|
20
|
+
rejects (`TypeError: __init__() argument 1 must be tree_sitter.Language,
|
|
21
|
+
not builtins.Language`), surfacing during ingest as `'bytes' object is
|
|
22
|
+
not an instance of 'str'`. This broke `code-memory` CLI ingest on Windows
|
|
23
|
+
across all install environments (uv-tool, global Python, watch daemon).
|
|
24
|
+
|
|
25
|
+
**Reason:** the loose `>=0.7` floor allowed the broken 1.9.1 wheel to
|
|
26
|
+
resolve on fresh Windows installs; pinning to the known-good 1.0.0
|
|
27
|
+
restores ingest. The pin lives in wheel metadata so every install path
|
|
28
|
+
resolves to the working version.
|
|
29
|
+
|
|
30
|
+
## [0.7.0] — 2026-06-12
|
|
31
|
+
|
|
32
|
+
Release theme: **Worktree resilience, atomic graph rebuilds, and fresh
|
|
33
|
+
indexes**. The index now survives interrupted full ingests; linked git
|
|
34
|
+
worktrees reuse the main repo's index instead of forcing a cold re-ingest;
|
|
35
|
+
and queries trigger non-blocking background rebuilds when the index drifts
|
|
36
|
+
from HEAD.
|
|
37
|
+
|
|
38
|
+
### Added — Linked git worktree awareness
|
|
39
|
+
|
|
40
|
+
**What:** a new `_git_toplevel()` helper resolves linked git worktrees
|
|
41
|
+
to the main repo's root by running `git rev-parse --git-common-dir`
|
|
42
|
+
after the baseline `--show-toplevel` call. In a linked worktree, the
|
|
43
|
+
two paths differ; in the main worktree, they're the same. The project
|
|
44
|
+
slug is now derived from the main repo's directory name, so all worktrees
|
|
45
|
+
of the same project reuse the same Qdrant / Falkor namespace without
|
|
46
|
+
requiring a cold re-ingest.
|
|
47
|
+
|
|
48
|
+
**Reason:** developers working in linked worktrees (e.g. `git worktree add
|
|
49
|
+
../feature-branch`) saw no code-memory functionality because the ingest
|
|
50
|
+
system minted a separate Qdrant collection and Falkor graph for each
|
|
51
|
+
worktree. A single repo with 3–5 active worktrees accumulated 3–5 cold
|
|
52
|
+
ingests. The main repo and each worktree now share the same index.
|
|
53
|
+
|
|
54
|
+
### Added — Non-persistent autostart for linked worktrees
|
|
55
|
+
|
|
56
|
+
**What:** two new gates in the autostart system:
|
|
57
|
+
|
|
58
|
+
- `is_linked_git_worktree(path)` — checks whether *path* is inside a
|
|
59
|
+
linked worktree via git CLI.
|
|
60
|
+
- `is_non_persistent_watch_dir(path)` — returns True for ephemeral
|
|
61
|
+
session dirs OR linked worktrees; used by `ensure_autostart()` to skip
|
|
62
|
+
registering a persistent OS agent for directories that are temporary or
|
|
63
|
+
share the main repo's watcher.
|
|
64
|
+
- `LaunchdAdapter.prune_stale()` (run on every MCP bootstrap) removes
|
|
65
|
+
launchd agents whose `WorkingDirectory` is gone or is a linked worktree.
|
|
66
|
+
|
|
67
|
+
**Reason:** the prior release fixed the watcher's per-session accumulation;
|
|
68
|
+
this release prevents the same bleed on linked worktrees. A developer with
|
|
69
|
+
2 linked worktrees no longer gets 3 persistent `code-memory watch` units.
|
|
70
|
+
|
|
71
|
+
### Added — Shadow-graph atomic promotion (graph durability fix)
|
|
72
|
+
|
|
73
|
+
**What:** the full ingest pipeline now builds into a shadow FalkorDB
|
|
74
|
+
graph named `<project_graph>__shadow` and atomically promotes it only
|
|
75
|
+
after the rebuild succeeds:
|
|
76
|
+
|
|
77
|
+
1. At ingest start, drop any leftover shadow from a prior interrupted
|
|
78
|
+
rebuild.
|
|
79
|
+
2. Redirect all graph writes to the shadow FalkorStore instance.
|
|
80
|
+
3. On successful completion, execute `GRAPH.DELETE <live>`, then
|
|
81
|
+
`GRAPH.COPY <shadow> <live>`, then `GRAPH.DELETE <shadow>`.
|
|
82
|
+
4. If the copy fails, the live graph is cleared but the shadow stays
|
|
83
|
+
intact — the caller can retry without losing data.
|
|
84
|
+
|
|
85
|
+
**Reason:** before this, an interrupted full ingest (network loss,
|
|
86
|
+
Ollama timeout, Falkor down, user kills the process) left the live
|
|
87
|
+
graph empty so subsequent `callers` / `definitions` / `callees` queries
|
|
88
|
+
returned nothing until a manual full re-ingest. The graph now survives
|
|
89
|
+
interruption — the shadow is cleaned up on the next rebuild attempt.
|
|
90
|
+
|
|
91
|
+
### Added — Health-check guard for stale rebuilds
|
|
92
|
+
|
|
93
|
+
**What:** the ingest state now records `file_count` and `symbol_count`
|
|
94
|
+
from each successful full rebuild. Before starting an incremental ingest,
|
|
95
|
+
`_health_check_ok()` compares the current ingestable file count against
|
|
96
|
+
the stored baseline; if it grew more than 20% and the graph symbol count
|
|
97
|
+
is suspiciously low (below a ratio threshold), a full rebuild is forced
|
|
98
|
+
with a diagnostic message to stderr.
|
|
99
|
+
|
|
100
|
+
Config:
|
|
101
|
+
- `CODE_MEMORY_INGEST_HEALTH_CHECK_ENABLED` (default `true`)
|
|
102
|
+
- `CODE_MEMORY_INGEST_HEALTH_CHECK_MIN_RATIO` (default `0.3` = expect ≥30%
|
|
103
|
+
of file count as symbol count)
|
|
104
|
+
|
|
105
|
+
**Reason:** a transient FalkorDB outage or an incomplete prior ingest
|
|
106
|
+
could leave the graph permanently empty while incremental ingests reported
|
|
107
|
+
success. The health check detects this silently-failed state and forces a
|
|
108
|
+
rebuild, with no user intervention needed.
|
|
109
|
+
|
|
110
|
+
### Added — Single-flight ingest lock
|
|
111
|
+
|
|
112
|
+
**What:** new `src/code_memory/sync/single_flight.py` module provides
|
|
113
|
+
in-process (`asyncio.Lock`) + cross-process (PID file) guards to prevent
|
|
114
|
+
concurrent full ingests for the same (root, project) pair.
|
|
115
|
+
|
|
116
|
+
- `try_acquire(root, project)` — returns True if no rebuild is running,
|
|
117
|
+
False if the slot is taken.
|
|
118
|
+
- `release(root, project)` — releases the slot unconditionally.
|
|
119
|
+
- Stale PID files (dead process or age > 30 min) are silently removed.
|
|
120
|
+
|
|
121
|
+
The Claude Code and Cursor plugins' `on-session-start.js` fast-path-skip a
|
|
122
|
+
spawn when a live ingest is detected, preventing thundering-herd `code-memory
|
|
123
|
+
ingest` calls on boot.
|
|
124
|
+
|
|
125
|
+
**Reason:** on slow machines or large repos, overlapping `code-memory ingest`
|
|
126
|
+
calls could queue up and queue up (especially if the embedder is I/O-bound),
|
|
127
|
+
causing a session to take 5+ minutes to boot. The lock ensures at most one
|
|
128
|
+
rebuild runs; fast-path skips spare the overhead.
|
|
129
|
+
|
|
130
|
+
### Added — Ingest safety guards
|
|
131
|
+
|
|
132
|
+
**What:** new `assert_safe_ingest_root()` function in `sync/safety.py`
|
|
133
|
+
refuses to ingest:
|
|
134
|
+
|
|
135
|
+
1. System/HOME roots (same set as the watcher guard: HOME, /, /tmp,
|
|
136
|
+
/var, /etc, /usr, /System, /Library, /opt, /Applications, C:/, etc.)
|
|
137
|
+
2. Non-git directories (checked via `is_inside_git_worktree()`).
|
|
138
|
+
|
|
139
|
+
Bypass via `CODE_MEMORY_UNSAFE_INGEST=1` env var (env-only, not a CLI
|
|
140
|
+
flag, to prevent accidental use).
|
|
141
|
+
|
|
142
|
+
**Reason:** `code-memory ingest ~` could walk every IDE cache, checkout,
|
|
143
|
+
and node_modules on disk. `ingest` is more dangerous than `watch` because
|
|
144
|
+
it stores results; a non-git directory ingest mints an arbitrary project
|
|
145
|
+
slug. The guard is invoked by the CLI `ingest` entry point and by hooks.
|
|
146
|
+
|
|
147
|
+
### Added — IPv4-default service URLs
|
|
148
|
+
|
|
149
|
+
**What:** the default `OLLAMA_URL`, `QDRANT_URL`, and `FALKOR_URL` now
|
|
150
|
+
use `127.0.0.1` instead of `localhost`. This works around a Windows
|
|
151
|
+
quirk where `localhost` may resolve to `::1` (IPv6) and hang on socket
|
|
152
|
+
connect.
|
|
153
|
+
|
|
154
|
+
Config example (all in `.code-memoryrc` or env):
|
|
155
|
+
```
|
|
156
|
+
OLLAMA_URL=http://127.0.0.1:11434
|
|
157
|
+
QDRANT_URL=http://127.0.0.1:6333
|
|
158
|
+
FALKOR_URL=redis://127.0.0.1:6379
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Reason:** on some Windows setups, the TCP stack prefers IPv6 and
|
|
162
|
+
binds localhost to `::1`, while the service listens on `127.0.0.1`.
|
|
163
|
+
Callers then hang waiting for a timeout (seconds to minutes). Using
|
|
164
|
+
the explicit IPv4 address is more reliable.
|
|
165
|
+
|
|
166
|
+
### Added — Non-blocking `_ensure_fresh` for MCP queries
|
|
167
|
+
|
|
168
|
+
**What:** the pre-query guard `_ensure_fresh()` no longer blocks on a
|
|
169
|
+
sync. Instead:
|
|
170
|
+
|
|
171
|
+
1. Spawn a quick freshness check (`_is_index_stale()`) in a bounded
|
|
172
|
+
daemon thread (`_FRESHNESS_PROBE_TIMEOUT`, default 2.0 s).
|
|
173
|
+
2. If the index is stale AND the check finished in time, fire a
|
|
174
|
+
background `_background_rebuild()` in a detached daemon thread
|
|
175
|
+
protected by the single-flight lock.
|
|
176
|
+
3. Return immediately so the query gets the current (possibly stale)
|
|
177
|
+
index while the rebuild runs in the background.
|
|
178
|
+
|
|
179
|
+
**Reason:** MCP queries were blocking on a full ingest in the worst case,
|
|
180
|
+
causing Claude Code / OpenCode / Cursor to hang for minutes. Now the agent
|
|
181
|
+
gets an answer immediately while background sync keeps the index fresh.
|
|
182
|
+
|
|
183
|
+
### Fixed — Ollama embed connect timeout
|
|
184
|
+
|
|
185
|
+
**What:** `OllamaEmbedder` now uses split connect/read timeouts:
|
|
186
|
+
|
|
187
|
+
- `_DEFAULT_CONNECT_TIMEOUT = 5.0 s` — fail fast on wrong stack (IPv6
|
|
188
|
+
vs IPv4) or misconfigured host.
|
|
189
|
+
- `_DEFAULT_READ_TIMEOUT = 300.0 s` — Ollama's cold-load model phase
|
|
190
|
+
happens during read, not connect.
|
|
191
|
+
|
|
192
|
+
Configurable via `OLLAMA_CONNECT_TIMEOUT` and `OLLAMA_READ_TIMEOUT`
|
|
193
|
+
env vars.
|
|
194
|
+
|
|
195
|
+
**Reason:** the old single `timeout=300` param applied to connect, which
|
|
196
|
+
could hang for 300 s waiting on a misconfigured IPv6 address. A 5 s
|
|
197
|
+
connect timeout with 3 retries fails fast (~15 s worst case) instead.
|
|
198
|
+
|
|
199
|
+
### Added — Vibe plugin
|
|
200
|
+
|
|
201
|
+
**What:** new `plugins/vibe/` brings code-memory to Mistral Vibe. Vibe
|
|
202
|
+
lacks lifecycle hooks (unlike Claude Code, Cursor, OpenCode), so the
|
|
203
|
+
plugin delivers:
|
|
204
|
+
|
|
205
|
+
- **Skill** (`/code-memory`) with orientation guidance and manual command
|
|
206
|
+
runner.
|
|
207
|
+
- **MCP server** registration in `config.toml`.
|
|
208
|
+
- **OS autostart watcher** for file-edit → auto-reingest (since hooks
|
|
209
|
+
aren't available).
|
|
210
|
+
|
|
211
|
+
Install: `./plugins/vibe/install.sh` (default user scope, with flags for
|
|
212
|
+
project scope, no-mcp, no-watch, uninstall).
|
|
213
|
+
|
|
214
|
+
**Reason:** Vibe is a code-aware LLM editor with a different extension
|
|
215
|
+
model. Bundling the same code-memory integration surfaces our topology
|
|
216
|
+
queries to Vibe users without reimplementation.
|
|
217
|
+
|
|
218
|
+
### Added — Ingest state enhancements
|
|
219
|
+
|
|
220
|
+
**What:** the ingest state now stores:
|
|
221
|
+
|
|
222
|
+
- `file_count` / `symbol_count` from each full rebuild (used by the
|
|
223
|
+
health check).
|
|
224
|
+
- `file_count` and `symbol_count` are populated by the pipeline during
|
|
225
|
+
ingest and read by the health-check predicate before deciding to
|
|
226
|
+
rebuild.
|
|
227
|
+
|
|
228
|
+
**Reason:** enables the health-check guard to detect silently-failed
|
|
229
|
+
ingests.
|
|
230
|
+
|
|
11
231
|
## [0.6.0] — 2026-06-04
|
|
12
232
|
|
|
13
233
|
Release theme: **Dart joins the graph, and `this.field.method()` resolves
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flurryx-code-memory
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.5
|
|
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
|
|
@@ -10,7 +10,7 @@ Requires-Dist: mcp>=1.0
|
|
|
10
10
|
Requires-Dist: pydantic>=2.8
|
|
11
11
|
Requires-Dist: qdrant-client>=1.12
|
|
12
12
|
Requires-Dist: rich>=13.7
|
|
13
|
-
Requires-Dist: tree-sitter-language-pack
|
|
13
|
+
Requires-Dist: tree-sitter-language-pack==1.0.0
|
|
14
14
|
Requires-Dist: tree-sitter>=0.23
|
|
15
15
|
Requires-Dist: typer>=0.12
|
|
16
16
|
Requires-Dist: watchdog>=4.0
|
|
@@ -99,9 +99,14 @@ curl -fsSL https://raw.githubusercontent.com/fmflurry/code-memory/main/install.s
|
|
|
99
99
|
**🪟 Windows**
|
|
100
100
|
|
|
101
101
|
```powershell
|
|
102
|
-
irm https://raw.githubusercontent.com/fmflurry/code-memory/main/install.ps1 | iex
|
|
102
|
+
irm https://raw.githubusercontent.com/fmflurry/code-memory/main/install.ps1 -Headers @{ 'Cache-Control' = 'no-cache' } | iex
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
If PowerShell reports a parser error at `install.ps1:392` showing the old
|
|
106
|
+
here-string terminator piped to `Write-Host`, you are running a stale
|
|
107
|
+
release/checkout. Update to `main`, or delete and re-download the installer
|
|
108
|
+
from the `main` URL above before running it.
|
|
109
|
+
|
|
105
110
|
The installer fetches the CLI, drops `docker-compose.yml` into `~/.code-memory/`, starts **FalkorDB** + **Qdrant**, pulls **`bge-m3`**, and wires the **Claude Code** + **OpenCode** plugins. Idempotent — safe to re-run.
|
|
106
111
|
|
|
107
112
|
### ✅ Verify
|
|
@@ -152,7 +157,7 @@ curl -fsSL https://raw.githubusercontent.com/fmflurry/code-memory/main/install.s
|
|
|
152
157
|
**Windows** — `iex` doesn't accept args, so download then run:
|
|
153
158
|
|
|
154
159
|
```powershell
|
|
155
|
-
iwr https://raw.githubusercontent.com/fmflurry/code-memory/main/install.ps1 -OutFile install.ps1
|
|
160
|
+
iwr https://raw.githubusercontent.com/fmflurry/code-memory/main/install.ps1 -Headers @{ 'Cache-Control' = 'no-cache' } -OutFile install.ps1
|
|
156
161
|
./install.ps1 -NoDocker -NoOllama -NoClaude -NoOpencode -NoMcp
|
|
157
162
|
```
|
|
158
163
|
|
|
@@ -68,6 +68,29 @@ param(
|
|
|
68
68
|
)
|
|
69
69
|
|
|
70
70
|
$ErrorActionPreference = 'Stop'
|
|
71
|
+
# PowerShell 7.3+ promotes native-command stderr to a terminating error when
|
|
72
|
+
# $ErrorActionPreference='Stop'. Docker CLI writes benign WARNINGs (e.g. the
|
|
73
|
+
# credential-plugin naming check) to stderr; we gate on $LASTEXITCODE instead,
|
|
74
|
+
# so do not let stderr alone abort the script.
|
|
75
|
+
$PSNativeCommandUseErrorActionPreference = $false
|
|
76
|
+
|
|
77
|
+
# Run a native command whose benign stderr (e.g. Docker CLI credential-plugin
|
|
78
|
+
# warnings) must NOT abort the script. Windows PowerShell 5.1 turns redirected
|
|
79
|
+
# native stderr into terminating NativeCommandError records under
|
|
80
|
+
# $ErrorActionPreference='Stop'; relaxing EAP for the call fixes it on 5.1 and
|
|
81
|
+
# 7+. Callers gate on $LASTEXITCODE. Returns nothing; sets $LASTEXITCODE.
|
|
82
|
+
function Invoke-NativeQuiet {
|
|
83
|
+
param([Parameter(Mandatory)][scriptblock] $Command)
|
|
84
|
+
$prevEAP = $ErrorActionPreference
|
|
85
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
86
|
+
try { & $Command 2>&1 | Out-Null } finally { $ErrorActionPreference = $prevEAP }
|
|
87
|
+
}
|
|
88
|
+
function Invoke-NativeVisible {
|
|
89
|
+
param([Parameter(Mandatory)][scriptblock] $Command)
|
|
90
|
+
$prevEAP = $ErrorActionPreference
|
|
91
|
+
$ErrorActionPreference = 'Continue'
|
|
92
|
+
try { & $Command 2>&1 | ForEach-Object { Write-Host $_ } } finally { $ErrorActionPreference = $prevEAP }
|
|
93
|
+
}
|
|
71
94
|
|
|
72
95
|
$RepoUrl = if ($env:CODEMEMORY_REPO_URL) { $env:CODEMEMORY_REPO_URL } else { 'https://github.com/fmflurry/code-memory' }
|
|
73
96
|
$RawUrl = if ($env:CODEMEMORY_RAW_URL) { $env:CODEMEMORY_RAW_URL } else { 'https://raw.githubusercontent.com/fmflurry/code-memory/main' }
|
|
@@ -185,7 +208,7 @@ if ($doDocker) {
|
|
|
185
208
|
Step "Starting FalkorDB + Qdrant"
|
|
186
209
|
if (Wait-ForCmd 'docker' 'Docker Desktop' 'https://www.docker.com/products/docker-desktop') {
|
|
187
210
|
# ensure daemon up
|
|
188
|
-
|
|
211
|
+
Invoke-NativeQuiet { docker info }
|
|
189
212
|
if ($LASTEXITCODE -ne 0) {
|
|
190
213
|
Warn "Docker CLI present but daemon not running. Start Docker Desktop."
|
|
191
214
|
if (Test-Interactive) {
|
|
@@ -195,7 +218,7 @@ if ($doDocker) {
|
|
|
195
218
|
}
|
|
196
219
|
}
|
|
197
220
|
if ($doDocker) {
|
|
198
|
-
|
|
221
|
+
Invoke-NativeVisible { docker compose -f (Join-Path $HomeDir 'docker/docker-compose.yml') --project-directory $HomeDir up -d }
|
|
199
222
|
if ($LASTEXITCODE -ne 0) {
|
|
200
223
|
Warn "docker compose up failed"
|
|
201
224
|
} else {
|
|
@@ -350,20 +373,21 @@ if ($doOpencode) {
|
|
|
350
373
|
|
|
351
374
|
# ---------- done ----------
|
|
352
375
|
Step "Done"
|
|
353
|
-
@
|
|
354
|
-
|
|
355
|
-
Side files: $HomeDir\
|
|
356
|
-
CLI: $(if ($cliPath) { $cliPath.Source } else { 'code-memory (not on PATH)' })
|
|
357
|
-
|
|
358
|
-
Ingest a repo:
|
|
359
|
-
code-memory ingest C:\path\to\repo
|
|
360
|
-
|
|
361
|
-
Query:
|
|
362
|
-
code-memory retrieve "where is the auth middleware?"
|
|
363
|
-
|
|
364
|
-
Browse:
|
|
365
|
-
FalkorDB http://localhost:3000
|
|
366
|
-
Qdrant http://localhost:6333/dashboard
|
|
367
|
-
|
|
368
|
-
Edit defaults: $HomeDir\.env
|
|
369
|
-
|
|
376
|
+
$doneLines = @(
|
|
377
|
+
''
|
|
378
|
+
" Side files: $HomeDir\"
|
|
379
|
+
" CLI: $(if ($cliPath) { $cliPath.Source } else { 'code-memory (not on PATH)' })"
|
|
380
|
+
''
|
|
381
|
+
' Ingest a repo:'
|
|
382
|
+
' code-memory ingest C:\path\to\repo'
|
|
383
|
+
''
|
|
384
|
+
' Query:'
|
|
385
|
+
' code-memory retrieve "where is the auth middleware?"'
|
|
386
|
+
''
|
|
387
|
+
' Browse:'
|
|
388
|
+
' FalkorDB http://localhost:3000'
|
|
389
|
+
' Qdrant http://localhost:6333/dashboard'
|
|
390
|
+
''
|
|
391
|
+
" Edit defaults: $HomeDir\.env"
|
|
392
|
+
)
|
|
393
|
+
Write-Host ($doneLines -join [Environment]::NewLine)
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const { execFile } = require("node:child_process");
|
|
10
|
+
const fs = require("node:fs");
|
|
11
|
+
const nodePath = require("node:path");
|
|
10
12
|
|
|
11
13
|
const DEFAULT_BINARY = process.env.CODE_MEMORY_BIN || "code-memory";
|
|
12
14
|
const DEFAULT_PROJECT = process.env.CODE_MEMORY_PROJECT || null;
|
|
@@ -39,6 +41,82 @@ async function detectAvailable(binary, log) {
|
|
|
39
41
|
return ok;
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Derive the lock directory used by code-memory's single_flight module.
|
|
46
|
+
* Mirrors the Python logic in sync/single_flight.py:_lock_dir().
|
|
47
|
+
* Returns null if the directory cannot be determined.
|
|
48
|
+
*/
|
|
49
|
+
function _lockDir() {
|
|
50
|
+
const override = process.env.CODE_MEMORY_LOCK_DIR;
|
|
51
|
+
if (override) return override;
|
|
52
|
+
const stateHome =
|
|
53
|
+
process.env.XDG_STATE_HOME ||
|
|
54
|
+
nodePath.join(
|
|
55
|
+
process.env.HOME || process.env.USERPROFILE || "",
|
|
56
|
+
".local",
|
|
57
|
+
"state",
|
|
58
|
+
);
|
|
59
|
+
return nodePath.join(stateHome, "code-memory", "locks");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Fast-path check: return true if a live ingest is already running for
|
|
64
|
+
* (resolvedRoot, slug). Mirrors Python single_flight._is_stale() logic:
|
|
65
|
+
* a lockfile is considered live when it exists, is not older than the TTL,
|
|
66
|
+
* and its PID is still running.
|
|
67
|
+
*
|
|
68
|
+
* This is JS-side best-effort only — the Python ingest entry point is the
|
|
69
|
+
* authoritative single-flight guard. We check here solely to avoid
|
|
70
|
+
* spawning a new process that would immediately lose the race.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} resolvedRoot - Absolute resolved path of the repo root.
|
|
73
|
+
* @param {string} slug - Project slug.
|
|
74
|
+
* @returns {boolean} true when a live ingest appears to be running.
|
|
75
|
+
*/
|
|
76
|
+
function _ingestLockLive(resolvedRoot, slug) {
|
|
77
|
+
try {
|
|
78
|
+
const lockDir = _lockDir();
|
|
79
|
+
if (!lockDir) return false;
|
|
80
|
+
|
|
81
|
+
// Replicate the Python filename derivation:
|
|
82
|
+
// name = f"{root_part[:64]}__{project_part[:32]}.lock"
|
|
83
|
+
const rootPart = resolvedRoot.replace(/[/\\]/g, "_").replace(/ /g, "_").slice(0, 64);
|
|
84
|
+
const slugPart = slug.replace(/\//g, "_").replace(/ /g, "_").slice(0, 32);
|
|
85
|
+
const lockFile = nodePath.join(lockDir, `${rootPart}__${slugPart}.lock`);
|
|
86
|
+
|
|
87
|
+
let stat;
|
|
88
|
+
try {
|
|
89
|
+
stat = fs.statSync(lockFile);
|
|
90
|
+
} catch {
|
|
91
|
+
return false; // file does not exist — no live ingest
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const ttl = parseFloat(process.env.CODE_MEMORY_REBUILD_LOCK_TTL || "3600");
|
|
95
|
+
const ageSecs = (Date.now() - stat.mtimeMs) / 1000;
|
|
96
|
+
if (ageSecs > ttl) return false; // stale by age
|
|
97
|
+
|
|
98
|
+
let pid;
|
|
99
|
+
try {
|
|
100
|
+
pid = parseInt(fs.readFileSync(lockFile, "utf8").trim(), 10);
|
|
101
|
+
} catch {
|
|
102
|
+
return false; // unreadable → treat as stale
|
|
103
|
+
}
|
|
104
|
+
if (!pid || isNaN(pid)) return false;
|
|
105
|
+
|
|
106
|
+
// Check if the PID is alive (POSIX: signal 0; Windows: tasklist not used,
|
|
107
|
+
// fall back to optimistic "assume live" to avoid a subprocess spawn).
|
|
108
|
+
try {
|
|
109
|
+
process.kill(pid, 0);
|
|
110
|
+
return true; // PID exists and is alive
|
|
111
|
+
} catch (e) {
|
|
112
|
+
if (e.code === "EPERM") return true; // exists but we lack permission
|
|
113
|
+
return false; // ESRCH — process is dead
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
return false; // any unexpected error → don't block
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
42
120
|
/**
|
|
43
121
|
* Spawn detached fire-and-forget. Parent exits immediately.
|
|
44
122
|
* stdout/stderr ignored. Used when the hook must not block.
|
|
@@ -115,6 +193,13 @@ async function createMemoryClient(opts = {}) {
|
|
|
115
193
|
|
|
116
194
|
ingestDetached({ full = false } = {}) {
|
|
117
195
|
if (!available) return false;
|
|
196
|
+
// Fast-path: skip spawn if the Python single-flight lock shows a live
|
|
197
|
+
// ingest is already running for this root. The CLI is the authoritative
|
|
198
|
+
// guard; this avoids spawning a process that would immediately lose the
|
|
199
|
+
// race and exit with code 0.
|
|
200
|
+
const resolvedCwd = nodePath.resolve(cwd);
|
|
201
|
+
const slug = project || nodePath.basename(resolvedCwd);
|
|
202
|
+
if (_ingestLockLive(resolvedCwd, slug)) return false;
|
|
118
203
|
return spawnDetached(
|
|
119
204
|
binary,
|
|
120
205
|
["ingest", cwd, "--json", ...(full ? ["--full"] : []), ...baseArgs(project)],
|
|
@@ -25,8 +25,18 @@ const { pruneExpired } = require("./lib/state");
|
|
|
25
25
|
const mem = await createMemoryClient({ cwd, log });
|
|
26
26
|
if (mem.available) {
|
|
27
27
|
// Ensure a launchd/systemd watcher unit exists for this repo so file
|
|
28
|
-
// edits between sessions trigger reingest automatically. Idempotent
|
|
29
|
-
//
|
|
28
|
+
// edits between sessions trigger reingest automatically. Idempotent.
|
|
29
|
+
//
|
|
30
|
+
// Both calls delegate to the `code-memory` binary (DEFAULT_BINARY /
|
|
31
|
+
// CODE_MEMORY_BIN — see lib/memory.js). The CLI enforces its own
|
|
32
|
+
// safety guards at the Python entry point:
|
|
33
|
+
// • `autostart install` — rejects HOME / system roots / ephemeral dirs
|
|
34
|
+
// via sync/safety.py:assert_safe_watch_root (wired in cli.py:watch).
|
|
35
|
+
// • `ingest` — rejects HOME / filesystem roots / non-git dirs via
|
|
36
|
+
// sync/safety.py:assert_safe_ingest_root (wired in cli.py:ingest).
|
|
37
|
+
// A single-flight PID lock also prevents concurrent ingests for the
|
|
38
|
+
// same root (sync/single_flight.py).
|
|
39
|
+
// These guards are install-version-independent (PyPI, uv tool, editable).
|
|
30
40
|
mem.autostartInstallDetached();
|
|
31
41
|
mem.ingestDetached();
|
|
32
42
|
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const { execFile } = require("node:child_process");
|
|
10
|
+
const fs = require("node:fs");
|
|
11
|
+
const nodePath = require("node:path");
|
|
10
12
|
|
|
11
13
|
const DEFAULT_BINARY = process.env.CODE_MEMORY_BIN || "code-memory";
|
|
12
14
|
const DEFAULT_PROJECT = process.env.CODE_MEMORY_PROJECT || null;
|
|
@@ -39,6 +41,82 @@ async function detectAvailable(binary, log) {
|
|
|
39
41
|
return ok;
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Derive the lock directory used by code-memory's single_flight module.
|
|
46
|
+
* Mirrors the Python logic in sync/single_flight.py:_lock_dir().
|
|
47
|
+
* Returns null if the directory cannot be determined.
|
|
48
|
+
*/
|
|
49
|
+
function _lockDir() {
|
|
50
|
+
const override = process.env.CODE_MEMORY_LOCK_DIR;
|
|
51
|
+
if (override) return override;
|
|
52
|
+
const stateHome =
|
|
53
|
+
process.env.XDG_STATE_HOME ||
|
|
54
|
+
nodePath.join(
|
|
55
|
+
process.env.HOME || process.env.USERPROFILE || "",
|
|
56
|
+
".local",
|
|
57
|
+
"state",
|
|
58
|
+
);
|
|
59
|
+
return nodePath.join(stateHome, "code-memory", "locks");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Fast-path check: return true if a live ingest is already running for
|
|
64
|
+
* (resolvedRoot, slug). Mirrors Python single_flight._is_stale() logic:
|
|
65
|
+
* a lockfile is considered live when it exists, is not older than the TTL,
|
|
66
|
+
* and its PID is still running.
|
|
67
|
+
*
|
|
68
|
+
* This is JS-side best-effort only — the Python ingest entry point is the
|
|
69
|
+
* authoritative single-flight guard. We check here solely to avoid
|
|
70
|
+
* spawning a new process that would immediately lose the race.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} resolvedRoot - Absolute resolved path of the repo root.
|
|
73
|
+
* @param {string} slug - Project slug.
|
|
74
|
+
* @returns {boolean} true when a live ingest appears to be running.
|
|
75
|
+
*/
|
|
76
|
+
function _ingestLockLive(resolvedRoot, slug) {
|
|
77
|
+
try {
|
|
78
|
+
const lockDir = _lockDir();
|
|
79
|
+
if (!lockDir) return false;
|
|
80
|
+
|
|
81
|
+
// Replicate the Python filename derivation:
|
|
82
|
+
// name = f"{root_part[:64]}__{project_part[:32]}.lock"
|
|
83
|
+
const rootPart = resolvedRoot.replace(/[/\\]/g, "_").replace(/ /g, "_").slice(0, 64);
|
|
84
|
+
const slugPart = slug.replace(/\//g, "_").replace(/ /g, "_").slice(0, 32);
|
|
85
|
+
const lockFile = nodePath.join(lockDir, `${rootPart}__${slugPart}.lock`);
|
|
86
|
+
|
|
87
|
+
let stat;
|
|
88
|
+
try {
|
|
89
|
+
stat = fs.statSync(lockFile);
|
|
90
|
+
} catch {
|
|
91
|
+
return false; // file does not exist — no live ingest
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const ttl = parseFloat(process.env.CODE_MEMORY_REBUILD_LOCK_TTL || "3600");
|
|
95
|
+
const ageSecs = (Date.now() - stat.mtimeMs) / 1000;
|
|
96
|
+
if (ageSecs > ttl) return false; // stale by age
|
|
97
|
+
|
|
98
|
+
let pid;
|
|
99
|
+
try {
|
|
100
|
+
pid = parseInt(fs.readFileSync(lockFile, "utf8").trim(), 10);
|
|
101
|
+
} catch {
|
|
102
|
+
return false; // unreadable → treat as stale
|
|
103
|
+
}
|
|
104
|
+
if (!pid || isNaN(pid)) return false;
|
|
105
|
+
|
|
106
|
+
// Check if the PID is alive (POSIX: signal 0; Windows: tasklist not used,
|
|
107
|
+
// fall back to optimistic "assume live" to avoid a subprocess spawn).
|
|
108
|
+
try {
|
|
109
|
+
process.kill(pid, 0);
|
|
110
|
+
return true; // PID exists and is alive
|
|
111
|
+
} catch (e) {
|
|
112
|
+
if (e.code === "EPERM") return true; // exists but we lack permission
|
|
113
|
+
return false; // ESRCH — process is dead
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
return false; // any unexpected error → don't block
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
42
120
|
/**
|
|
43
121
|
* Spawn detached fire-and-forget. Parent exits immediately.
|
|
44
122
|
* stdout/stderr ignored. Used when the hook must not block.
|
|
@@ -115,6 +193,13 @@ async function createMemoryClient(opts = {}) {
|
|
|
115
193
|
|
|
116
194
|
ingestDetached({ full = false } = {}) {
|
|
117
195
|
if (!available) return false;
|
|
196
|
+
// Fast-path: skip spawn if the Python single-flight lock shows a live
|
|
197
|
+
// ingest is already running for this root. The CLI is the authoritative
|
|
198
|
+
// guard; this avoids spawning a process that would immediately lose the
|
|
199
|
+
// race and exit with code 0.
|
|
200
|
+
const resolvedCwd = nodePath.resolve(cwd);
|
|
201
|
+
const slug = project || nodePath.basename(resolvedCwd);
|
|
202
|
+
if (_ingestLockLive(resolvedCwd, slug)) return false;
|
|
118
203
|
return spawnDetached(
|
|
119
204
|
binary,
|
|
120
205
|
["ingest", cwd, "--json", ...(full ? ["--full"] : []), ...baseArgs(project)],
|
{flurryx_code_memory-0.6.2 → flurryx_code_memory-0.7.5}/plugins/cursor/scripts/on-session-start.js
RENAMED
|
@@ -21,6 +21,16 @@ const { pruneExpired } = require("./lib/state");
|
|
|
21
21
|
|
|
22
22
|
const mem = await createMemoryClient({ cwd, log: () => {} });
|
|
23
23
|
if (mem.available) {
|
|
24
|
+
// Both calls delegate to the `code-memory` binary (DEFAULT_BINARY /
|
|
25
|
+
// CODE_MEMORY_BIN — see lib/memory.js). The CLI enforces its own
|
|
26
|
+
// safety guards at the Python entry point:
|
|
27
|
+
// • `autostart install` — rejects HOME / system roots / ephemeral dirs
|
|
28
|
+
// via sync/safety.py:assert_safe_watch_root (wired in cli.py:watch).
|
|
29
|
+
// • `ingest` — rejects HOME / filesystem roots / non-git dirs via
|
|
30
|
+
// sync/safety.py:assert_safe_ingest_root (wired in cli.py:ingest).
|
|
31
|
+
// A single-flight PID lock also prevents concurrent ingests for the
|
|
32
|
+
// same root (sync/single_flight.py).
|
|
33
|
+
// These guards are install-version-independent (PyPI, uv tool, editable).
|
|
24
34
|
mem.ingestDetached();
|
|
25
35
|
mem.autostartInstallDetached();
|
|
26
36
|
}
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flurryx-code-memory"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.5"
|
|
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 = [
|
|
@@ -12,7 +12,7 @@ dependencies = [
|
|
|
12
12
|
"qdrant-client>=1.12",
|
|
13
13
|
"falkordb>=1.0.10",
|
|
14
14
|
"tree-sitter>=0.23",
|
|
15
|
-
"tree-sitter-language-pack
|
|
15
|
+
"tree-sitter-language-pack==1.0.0",
|
|
16
16
|
"pydantic>=2.8",
|
|
17
17
|
"typer>=0.12",
|
|
18
18
|
"rich>=13.7",
|