contextual-engine 0.8.6__tar.gz → 0.8.9__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 (263) hide show
  1. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/.github/workflows/ci.yml +1 -15
  2. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/.gitignore +1 -0
  3. contextual_engine-0.8.9/.mcp.json +11 -0
  4. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/PKG-INFO +1 -1
  5. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/__init__.py +2 -1
  6. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/__init__.py +2 -3
  7. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/client.py +52 -13
  8. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/config.py +3 -3
  9. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/doctor.py +8 -10
  10. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/index.py +0 -1
  11. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/install.py +1 -2
  12. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/mcp.py +9 -6
  13. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/stats.py +11 -10
  14. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/workspace.py +2 -1
  15. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/core/output.py +11 -10
  16. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/git/incremental_git.py +15 -5
  17. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/integrator.py +1 -3
  18. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/resolver.py +2 -2
  19. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/staleness.py +3 -4
  20. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/traversal.py +0 -3
  21. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/incremental/daemon_embed.py +0 -4
  22. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/incremental/incremental_indexer.py +3 -0
  23. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/chunker.py +1 -4
  24. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/pipeline.py +1 -1
  25. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/integrations/client_content.py +0 -1
  26. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/__main__.py +0 -2
  27. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/_platform.py +1 -2
  28. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/config.py +1 -1
  29. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/middleware/audit.py +6 -5
  30. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/middleware/ratelimit.py +2 -3
  31. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/middleware/warmup.py +6 -3
  32. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/server.py +18 -3
  33. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/shim.py +79 -14
  34. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/tools/graph.py +50 -18
  35. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/tools/nexus.py +18 -6
  36. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/tools/semantic.py +36 -10
  37. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/tools/system.py +6 -3
  38. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/tools/temporal.py +39 -13
  39. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/temporal/blame_pipeline.py +2 -2
  40. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/temporal/context.py +3 -6
  41. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/pyproject.toml +1 -1
  42. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/conftest.py +1 -5
  43. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_accuracy.py +0 -7
  44. contextual_engine-0.8.9/tests/test_architecture.py +23 -0
  45. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_async.py +0 -6
  46. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_behaviour.py +0 -6
  47. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_flow.py +0 -6
  48. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_git.py +0 -9
  49. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_latency.py +0 -9
  50. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_mcp.py +0 -10
  51. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_mcp_hardening.py +0 -1
  52. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_observability.py +0 -9
  53. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_performance.py +0 -10
  54. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_retrieval.py +0 -4
  55. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_retry.py +0 -6
  56. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_security.py +0 -6
  57. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_storage.py +7 -6
  58. contextual_engine-0.8.6/.mcp.json +0 -8
  59. contextual_engine-0.8.6/tests/test_architecture.py +0 -43
  60. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/.contextualignore +0 -0
  61. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/.github/workflows/release.yml +0 -0
  62. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/LICENSE +0 -0
  63. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/README.md +0 -0
  64. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/__main__.py +0 -0
  65. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/__init__.py +0 -0
  66. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/auth.py +0 -0
  67. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/fetch.py +0 -0
  68. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/commands/initialisation.py +1 -1
  69. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/core/__init__.py +0 -0
  70. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/core/context.py +0 -0
  71. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/core/decorators.py +0 -0
  72. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/cli/core/editor.py +0 -0
  73. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/__init__.py +0 -0
  74. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/base.py +0 -0
  75. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/global_/__init__.py +2 -2
  76. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/global_/mcp_config.py +0 -0
  77. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/global_/security_config.py +0 -0
  78. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/global_/storage_config.py +0 -0
  79. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/indexing.py +0 -0
  80. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/paths.py +0 -0
  81. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/workspace/__init__.py +0 -0
  82. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/workspace/cache_config.py +0 -0
  83. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/workspace/indexing_config.py +0 -0
  84. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/workspace/observability_config.py +0 -0
  85. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/workspace/retrieval_config.py +0 -0
  86. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/config/workspace/workspace_config.py +0 -0
  87. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/core/__init__.py +0 -0
  88. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/core/errors.py +0 -0
  89. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/core/models.py +0 -0
  90. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/git/__init__.py +0 -0
  91. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/git/blame.py +0 -0
  92. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/git/hooks.py +0 -0
  93. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/__init__.py +0 -0
  94. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/dedup.py +0 -0
  95. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/extractor.py +0 -0
  96. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/graph/store.py +0 -0
  97. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/incremental/__init__.py +0 -0
  98. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/incremental/file_watcher.py +0 -0
  99. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/__init__.py +0 -0
  100. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/_config.py +0 -0
  101. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/docs_pipeline.py +0 -0
  102. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/index_writer.py +0 -0
  103. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/merkle_tree.py +0 -0
  104. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/processor.py +0 -0
  105. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/indexing/symbol_extractor.py +0 -0
  106. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/integrations/__init__.py +0 -0
  107. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/integrations/client_writer.py +1 -1
  108. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/__init__.py +0 -0
  109. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/access.py +0 -0
  110. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/daemon.py +0 -0
  111. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/middleware/__init__.py +0 -0
  112. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/middleware/auth.py +0 -0
  113. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/middleware/identity.py +0 -0
  114. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/middleware/workspace.py +0 -0
  115. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/router.py +2 -2
  116. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/session_manager.py +0 -0
  117. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/mcp/tools/__init__.py +1 -1
  118. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/models/__init__.py +0 -0
  119. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/models/_inference.py +0 -0
  120. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/models/allowlist.py +0 -0
  121. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/models/base_embedder.py +0 -0
  122. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/models/reranker_model.py +0 -0
  123. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/models/unified_embedder.py +0 -0
  124. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/observability/__init__.py +0 -0
  125. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/observability/config.py +0 -0
  126. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/observability/exporters.py +0 -0
  127. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/observability/logging.py +0 -0
  128. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/observability/retention.py +0 -0
  129. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/observability/tracer.py +0 -0
  130. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/retrieval/__init__.py +0 -0
  131. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/retrieval/context_assembler.py +0 -0
  132. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/retrieval/mmr.py +0 -0
  133. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/retrieval/pipeline.py +0 -0
  134. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/retrieval/ranker.py +0 -0
  135. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/retrieval/tokenizer.py +0 -0
  136. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/security/__init__.py +0 -0
  137. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/security/paths.py +0 -0
  138. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/security/sanitize.py +0 -0
  139. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/security/workspace.py +0 -0
  140. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/__init__.py +0 -0
  141. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/cache.py +0 -0
  142. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/connection.py +0 -0
  143. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/fts.py +0 -0
  144. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/lancedb_provider.py +0 -0
  145. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/__init__.py +0 -0
  146. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me001_orgs.py +0 -0
  147. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me002_org_members.py +0 -0
  148. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me003_teams.py +0 -0
  149. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me004_team_members.py +0 -0
  150. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me005_org_workspaces.py +0 -0
  151. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me006_workspace_permissions.py +0 -0
  152. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me007_idp_sync_log.py +0 -0
  153. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me008_org_policies.py +0 -0
  154. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me009_api_keys.py +0 -0
  155. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me010_org_audit_log.py +0 -0
  156. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/enterprise/me011_workspace_traceability.py +0 -0
  157. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m001_global.py +0 -0
  158. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m002_embeddings.py +0 -0
  159. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m003_temporal.py +0 -0
  160. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m004_graph.py +0 -0
  161. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m005_cache.py +0 -0
  162. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m006_logs.py +0 -0
  163. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m007_terminal_sessions.py +0 -0
  164. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m008_queue.py +0 -0
  165. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m009_swarm.py +0 -0
  166. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m010_task_force.py +0 -0
  167. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m011_reminders.py +0 -0
  168. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m013_temporal_l2.py +0 -0
  169. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/m014_nexus_foundation.py +0 -0
  170. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/runner.py +0 -0
  171. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m001_global.py +0 -0
  172. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m002_embeddings.py +0 -0
  173. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m003_temporal.py +0 -0
  174. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m004_graph.py +0 -0
  175. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m005_cache.py +0 -0
  176. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m006_logs.py +0 -0
  177. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m007_terminal_sessions.py +0 -0
  178. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m008_queue.py +0 -0
  179. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m009_swarm.py +0 -0
  180. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m010_task_force.py +0 -0
  181. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m011_reminders.py +0 -0
  182. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m012_workspace_metadata.py +0 -0
  183. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m013_temporal_l2.py +0 -0
  184. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/solo/m014_nexus_foundation.py +1 -1
  185. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt001_orgs.py +0 -0
  186. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt002_org_members.py +0 -0
  187. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt003_teams.py +0 -0
  188. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt004_team_members.py +0 -0
  189. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt005_team_workspaces.py +0 -0
  190. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt006_workspace_permissions.py +0 -0
  191. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt007_team_audit_log.py +0 -0
  192. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/migrations/teams/mt008_workspace_traceability.py +0 -0
  193. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/schema.py +0 -0
  194. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/schema_base.py +0 -0
  195. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/schema_ent.py +0 -0
  196. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/storage/schema_team.py +0 -0
  197. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/temporal/__init__.py +0 -0
  198. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/temporal/adr.py +0 -0
  199. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/temporal/query.py +0 -0
  200. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/contextual/temporal/velocity.py +0 -0
  201. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/ADRs/ADR-001-lancedb-over-vec0.md +0 -0
  202. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/ADRs/ADR-002-retry-exponential-backoff.md +0 -0
  203. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/ADRs/ADR-003-blake3-deduplication.md +0 -0
  204. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/ADRs/ADR-004-async-first-architecture.md +0 -0
  205. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/ADRs/ADR-005-model-lineup.md +0 -0
  206. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/ARCHITECTURE.md +0 -0
  207. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/CHANGELOG.md +0 -0
  208. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DATABASE.md +0 -0
  209. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DEPLOYMENT.md +0 -0
  210. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DISTRIBUTIONS.md +0 -0
  211. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/CHATGPT-DR-mcp-cli-security-testing-distribution.md +0 -0
  212. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/CHATGPT-DR-web-ui-experience.md +0 -0
  213. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/CLAUDE-DR-ai-agents-teams.md +0 -0
  214. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/CLAUDE-DR-competitive-landscape-market-analysis.md +0 -0
  215. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/CLAUDE-DR-core-architecture.md +0 -0
  216. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/CLAUDE-DR-mvp-phase0-technical-plan.md +0 -0
  217. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/CLAUDE-DR-strategic-briefing.md +0 -0
  218. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GEMINI-DR-graph-implementation.md +0 -0
  219. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GEMINI-DR-hybrid-retrieval-local-optimization.md +0 -0
  220. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GEMINI-DR-infrastructure-security.md +0 -0
  221. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GEMINI-DR-lance-specs.md +0 -0
  222. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GEMINI-DR-nexus-graph-architecture.md +0 -0
  223. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GEMINI-DR-nexus-graph-engineering.md +0 -0
  224. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GEMINI-DR-vec0-vs-lancedb.md +0 -0
  225. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/GRAPH_LAYER_OPEN_QUESTIONS_RESOLVED.md +0 -0
  226. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/KIMI-DR-tech-stack-optimization.md +0 -0
  227. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-L2-temporal.md +0 -0
  228. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-client-integration.md +0 -0
  229. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-indexing-optimisation.md +0 -0
  230. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-lancedb-implementation.md +0 -0
  231. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-local-data-infrastructure.md +0 -0
  232. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-mcp-config.md +0 -0
  233. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-mcp-security-hardening.md +0 -0
  234. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-models-pipelines.md +0 -0
  235. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-python-async-ai-pipelines.md +0 -0
  236. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/DRs/PERPLEXITY-DR-semantic-indexing.md +0 -0
  237. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/FRONTEND.md +0 -0
  238. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/GITHUB.md +0 -0
  239. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/INTEGRATIONS.md +0 -0
  240. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/MCP_TOOLS.md +0 -0
  241. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/MEMORY.md +0 -0
  242. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/PROJECT_MANAGEMENT.md +0 -0
  243. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/PROJECT_PHASES.md +0 -0
  244. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/RESOURCES.md +0 -0
  245. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/ROADMAP.md +0 -0
  246. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/SECURITY.md +0 -0
  247. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/TEAMS.md +0 -0
  248. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/TECHNICAL_SPEC.md +0 -0
  249. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/TESTING.md +0 -0
  250. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/docs/VERSION.MD +0 -0
  251. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/pytest.ini +0 -0
  252. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/README.md +0 -0
  253. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/__init__.py +0 -0
  254. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/fixtures/__init__.py +0 -0
  255. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/fixtures/sample_code.py +0 -0
  256. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/fixtures/sample_docs.md +0 -0
  257. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_cache_stats.py +0 -0
  258. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_cli.py +0 -0
  259. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_graph.py +0 -0
  260. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_graph_resolution_order.py +0 -0
  261. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_incremental.py +0 -0
  262. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_indexing.py +0 -0
  263. {contextual_engine-0.8.6 → contextual_engine-0.8.9}/tests/test_query_cache_roundtrip.py +0 -0
@@ -32,18 +32,4 @@ jobs:
32
32
  cache-dependency-glob: uv.lock
33
33
 
34
34
  - name: Install dependencies
35
- run: uv sync
36
-
37
- - name: Ruff check
38
- run: uv run ruff check .
39
-
40
- - name: Ruff format check
41
- run: uv run ruff format --check .
42
-
43
- - name: Mypy
44
- run: uv run mypy contextual/ --strict
45
-
46
- - name: Pytest
47
- env:
48
- NUMBA_DISABLE_JIT: "1"
49
- run: uv run pytest tests/
35
+ run: uv sync --extra dev --extra test
@@ -105,3 +105,4 @@ SCAFFOLD_README.md
105
105
  output.md
106
106
  audit_report.mduv.lock
107
107
  audit.md
108
+ .agent
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers": {
3
+ "contextual": {
4
+ "command": "/Users/apple/.pyenv/versions/3.12.10/bin/contextual-mcp",
5
+ "args": [
6
+ "--workspace",
7
+ "/Users/apple/TechStack/Projects/Contextual"
8
+ ]
9
+ }
10
+ }
11
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: contextual-engine
3
- Version: 0.8.6
3
+ Version: 0.8.9
4
4
  Summary: Temporal-first local code memory for AI tools via MCP
5
5
  Project-URL: Homepage, https://contextuallabs.dev
6
6
  Project-URL: Documentation, https://contextuallabs.dev/docs
@@ -6,7 +6,8 @@ through bi-temporal fact tracking and deterministic retrieval.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- from importlib.metadata import PackageNotFoundError, version as _pkg_version
9
+ from importlib.metadata import PackageNotFoundError
10
+ from importlib.metadata import version as _pkg_version
10
11
 
11
12
  try:
12
13
  __version__ = _pkg_version("contextual-engine")
@@ -15,6 +15,7 @@ from typing import Any
15
15
 
16
16
  import typer
17
17
 
18
+ from contextual import __version__ # single source: pyproject.toml via importlib.metadata
18
19
  from contextual.cli import commands
19
20
  from contextual.cli.core.decorators import _COMMAND_REGISTRY
20
21
  from contextual.cli.core.output import (
@@ -24,8 +25,6 @@ from contextual.cli.core.output import (
24
25
  print_version,
25
26
  )
26
27
 
27
- from contextual import __version__ # single source: pyproject.toml via importlib.metadata
28
-
29
28
  # ── Brand theme: override Typer's default Rich help colors ────────────
30
29
  # Must run before typer.Typer() is instantiated.
31
30
  try:
@@ -54,7 +53,7 @@ try:
54
53
  _ru.STYLE_ERRORS_SUGGESTION = "#6b6b6b"
55
54
  _ru.STYLE_ERRORS_SUGGESTION_COMMAND = "white bold"
56
55
  _ru.STYLE_ABORTED = "#6b6b6b"
57
-
56
+
58
57
  # Custom additions to ensure no yellow/cyan is shown
59
58
  _ru.STYLE_COMMANDS_TABLE_FIRST_COLUMN = "white"
60
59
  _ru.STYLE_DEPRECATED = "#6b6b6b"
@@ -14,29 +14,29 @@ from typing import Annotated
14
14
 
15
15
  import typer
16
16
 
17
- from contextual.cli.core import register_command, require_config, open_in_editor
17
+ from contextual.cli.core import open_in_editor, register_command, require_config
18
18
  from contextual.cli.core.output import (
19
19
  console,
20
+ print_agents_md_hint,
20
21
  print_client_configured,
22
+ print_client_list,
21
23
  print_client_removed,
22
24
  print_error,
23
25
  print_info,
24
26
  print_md_injected,
25
27
  print_md_removed,
26
- print_agents_md_hint,
27
- print_client_list,
28
28
  print_rm_not_configured,
29
29
  )
30
30
  from contextual.integrations.client_content import (
31
- cursor_file_content,
32
31
  antigravity_file_content,
32
+ cursor_file_content,
33
33
  marker_block,
34
34
  )
35
35
  from contextual.integrations.client_writer import (
36
- write_type_a,
37
- write_type_b,
38
36
  remove_type_a,
39
37
  remove_type_b,
38
+ write_type_a,
39
+ write_type_b,
40
40
  )
41
41
 
42
42
 
@@ -223,8 +223,30 @@ def _write_client_config(
223
223
  return target, ("written" if found_key is None else "updated")
224
224
 
225
225
 
226
+ def _find_contextual_mcp() -> str | None:
227
+ """Locate the pip-installed contextual-mcp binary.
228
+
229
+ Looks next to sys.executable first — this is the binary that was installed
230
+ alongside the package in the user's active Python environment (pyenv, conda,
231
+ virtualenv, plain pip). That lookup is independent of PATH, so it works
232
+ even in AI clients that inherit a minimal environment. Falls back to a
233
+ PATH search for edge cases. Returns None when neither finds it, in which
234
+ case callers should fall back to uvx.
235
+ """
236
+ sibling = Path(sys.executable).resolve().parent / "contextual-mcp"
237
+ if sibling.exists():
238
+ return str(sibling)
239
+ return shutil.which("contextual-mcp")
240
+
241
+
226
242
  def _build_mcp_entry(uvx_path: str, workspace_root: Path) -> dict:
227
243
  """Build the default MCP launcher entry for Contextual."""
244
+ local = _find_contextual_mcp()
245
+ if local:
246
+ return {
247
+ "command": local,
248
+ "args": ["--workspace", str(workspace_root)],
249
+ }
228
250
  return {
229
251
  "command": uvx_path,
230
252
  "args": ["--from", "contextual-engine", "contextual-mcp", "--workspace", str(workspace_root)],
@@ -232,7 +254,15 @@ def _build_mcp_entry(uvx_path: str, workspace_root: Path) -> dict:
232
254
 
233
255
 
234
256
  def _build_claude_mcp_entry(uvx_path: str, workspace_root: Path) -> dict:
235
- """Build the Claude Desktop MCP entry with an explicit workspace cd."""
257
+ """Build the Claude Desktop MCP entry."""
258
+ local = _find_contextual_mcp()
259
+ if local:
260
+ return {
261
+ "command": local,
262
+ "args": ["--workspace", str(workspace_root)],
263
+ }
264
+ # uvx fallback: Claude Desktop has a minimal PATH so wrap in zsh -lc to
265
+ # ensure uvx is findable before exec-ing.
236
266
  return {
237
267
  "command": "/bin/zsh",
238
268
  "args": [
@@ -252,6 +282,13 @@ def _build_vscode_mcp_entry(uvx_path: str, workspace_root: Path) -> dict:
252
282
  VS Code requires an explicit ``"type": "stdio"`` field — other clients
253
283
  accept entries without it.
254
284
  """
285
+ local = _find_contextual_mcp()
286
+ if local:
287
+ return {
288
+ "type": "stdio",
289
+ "command": local,
290
+ "args": ["--workspace", str(workspace_root)],
291
+ }
255
292
  return {
256
293
  "type": "stdio",
257
294
  "command": uvx_path,
@@ -371,10 +408,12 @@ def _write_codex_toml_config(
371
408
  original = copy.deepcopy(existing)
372
409
  mcp_servers = existing.setdefault("mcp_servers", {})
373
410
  ws_str = str(workspace_root.resolve())
374
- entry = {
375
- "command": uvx_path,
376
- "args": ["--from", "contextual-engine", "contextual-mcp", "--workspace", str(workspace_root)],
377
- }
411
+ local = _find_contextual_mcp()
412
+ entry = (
413
+ {"command": local, "args": ["--workspace", str(workspace_root)]}
414
+ if local
415
+ else {"command": uvx_path, "args": ["--from", "contextual-engine", "contextual-mcp", "--workspace", str(workspace_root)]}
416
+ )
378
417
 
379
418
  # TOML entries embed the workspace in args (no shell wrapper), so resolve
380
419
  # the bound workspace from the --workspace arg rather than via the shared
@@ -535,8 +574,8 @@ def _display_manual_config(
535
574
  ui_steps: list[str],
536
575
  ) -> None:
537
576
  """Print manual setup instructions for UI-based MCP clients."""
538
- from rich.syntax import Syntax
539
577
  from rich.panel import Panel
578
+ from rich.syntax import Syntax
540
579
  from rich.text import Text
541
580
 
542
581
  cmd = (
@@ -594,7 +633,7 @@ def _config_paths() -> dict[str, Path]:
594
633
  "copilot": home / ".vscode" / "mcp.json",
595
634
  "windsurf": home / ".codeium" / "windsurf" / "mcp_config.json",
596
635
  "codex": home / ".codex" / "config.toml",
597
- "antigravity": home / ".gemini" / "config" / "mcp_config.json",
636
+ "antigravity": home / ".antigravity" / "mcp.json",
598
637
  }
599
638
 
600
639
 
@@ -4,13 +4,13 @@ from __future__ import annotations
4
4
 
5
5
  import tomllib
6
6
  import uuid
7
- from datetime import datetime, UTC
7
+ from datetime import UTC, datetime
8
8
  from pathlib import Path
9
- from typing import Any
9
+
10
10
  import tomli_w
11
11
  import typer
12
12
 
13
- from contextual.cli.core import get_context, register_command, require_config, open_in_editor
13
+ from contextual.cli.core import get_context, open_in_editor, register_command, require_config
14
14
  from contextual.cli.core.output import print_error, print_info, print_success, print_warning
15
15
  from contextual.config.paths import GLOBAL_CONFIG, workspace_config
16
16
  from contextual.config.workspace.workspace_config import WorkspaceConfig
@@ -3,25 +3,23 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import os
6
- import shutil
7
- import sys
8
- from pathlib import Path
6
+
9
7
  import typer
10
8
 
11
9
  from contextual.cli.core import get_context, register_command
12
- from contextual.cli.core.output import HealthCheck, print_doctor, print_error
13
- from contextual.config.paths import MCP_CONFIG_FILE as MCP_JSON_PATH
10
+ from contextual.cli.core.output import HealthCheck, print_doctor
14
11
  from contextual.config.paths import (
15
12
  GLOBAL_CONFIG,
16
13
  GLOBAL_DB_DIR,
17
- MODELS_DIR,
18
14
  LOG_DIR,
15
+ MODELS_DIR,
19
16
  embed_model_present,
20
17
  rerank_model_present,
21
18
  workspace_config,
22
19
  workspace_db,
23
20
  workspace_logs,
24
21
  )
22
+ from contextual.config.paths import MCP_CONFIG_FILE as MCP_JSON_PATH
25
23
 
26
24
 
27
25
  @register_command()
@@ -37,7 +35,7 @@ async def doctor(
37
35
  global_cfg_ok = GLOBAL_CONFIG.exists()
38
36
  ws_cfg_path = workspace_config(workspace_path)
39
37
  ws_cfg_ok = ws_cfg_path.exists()
40
-
38
+
41
39
  if not global_cfg_ok:
42
40
  config_status = False
43
41
  config_detail = "Missing global config. Run 'contextual setup'."
@@ -64,7 +62,7 @@ async def doctor(
64
62
  dirs_ok = True
65
63
  missing_dirs = []
66
64
  unwritable_dirs = []
67
-
65
+
68
66
  for name, path in dirs_to_check:
69
67
  if not path.exists():
70
68
  dirs_ok = False
@@ -101,7 +99,7 @@ async def doctor(
101
99
  daemon_running = False
102
100
  lock_detail = "Daemon not running."
103
101
  try:
104
- from contextual.mcp.daemon import read_lock, is_running
102
+ from contextual.mcp.daemon import is_running, read_lock
105
103
  lock = read_lock()
106
104
  if lock and is_running(lock):
107
105
  daemon_running = True
@@ -114,7 +112,7 @@ async def doctor(
114
112
  db_ok = GLOBAL_DB_DIR.exists()
115
113
  ws_db_path = workspace_db(workspace_path)
116
114
  ws_db_ok = ws_db_path.exists()
117
-
115
+
118
116
  if db_ok:
119
117
  db_detail = "Global DB OK."
120
118
  if ws_db_ok:
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import asyncio
6
5
  import contextlib
7
6
  import logging
8
7
  import os
@@ -10,7 +10,6 @@ from datetime import UTC, datetime
10
10
  from pathlib import Path
11
11
 
12
12
  import structlog
13
-
14
13
  import typer
15
14
 
16
15
  from contextual import __version__ as current_version
@@ -394,7 +393,7 @@ def hooks_install(path: str = "") -> None:
394
393
  print_info(f"{icon} {hook_name} {status}")
395
394
 
396
395
  print_success(
397
- f"Hooks installed",
396
+ "Hooks installed",
398
397
  detail=f"{repo_root / '.git' / 'hooks'} · auto-indexes on commit, checkout, merge",
399
398
  )
400
399
 
@@ -3,16 +3,19 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import time
6
+
6
7
  import typer
7
- from contextual.cli.core import register_command, open_in_editor
8
+
9
+ from contextual.cli.core import open_in_editor, register_command
8
10
  from contextual.cli.core.output import (
11
+ console,
9
12
  print_error,
10
13
  print_info,
11
14
  print_success,
12
15
  print_warning,
13
- console,
14
16
  )
15
- from contextual.config.paths import MCP_CONFIG_FILE as MCP_JSON_PATH, DAEMON_LOG
17
+ from contextual.config.paths import DAEMON_LOG
18
+ from contextual.config.paths import MCP_CONFIG_FILE as MCP_JSON_PATH
16
19
 
17
20
 
18
21
  @register_command(group="mcp")
@@ -81,7 +84,7 @@ def mcp_start() -> None:
81
84
  @register_command(group="mcp")
82
85
  def mcp_stop() -> None:
83
86
  """Stop the running MCP daemon."""
84
- from contextual.mcp.daemon import stop_daemon, read_lock
87
+ from contextual.mcp.daemon import read_lock, stop_daemon
85
88
 
86
89
  lock = read_lock()
87
90
  if not lock:
@@ -135,7 +138,7 @@ def mcp_logs(
135
138
  return
136
139
 
137
140
  try:
138
- with open(DAEMON_LOG, "r", encoding="utf-8") as f:
141
+ with open(DAEMON_LOG, encoding="utf-8") as f:
139
142
  all_lines = f.readlines()
140
143
  for line in all_lines[-lines:]:
141
144
  console.print(line, highlight=False, end="")
@@ -145,7 +148,7 @@ def mcp_logs(
145
148
 
146
149
  if follow:
147
150
  try:
148
- with open(DAEMON_LOG, "r", encoding="utf-8") as f:
151
+ with open(DAEMON_LOG, encoding="utf-8") as f:
149
152
  f.seek(0, 2)
150
153
  while True:
151
154
  line = f.readline()
@@ -4,15 +4,16 @@ from __future__ import annotations
4
4
 
5
5
  from datetime import UTC
6
6
  from typing import Any
7
+
7
8
  import typer
8
9
 
9
10
  from contextual.cli.core import get_context, register_command, require_config, require_index
10
11
  from contextual.cli.core.output import (
11
12
  print_error,
12
13
  print_stats,
13
- print_version,
14
- print_stats_temporal,
15
14
  print_stats_graph,
15
+ print_stats_temporal,
16
+ print_version,
16
17
  )
17
18
  from contextual.security.sanitize import sql_str_literal
18
19
 
@@ -96,7 +97,7 @@ async def stats(
96
97
  .where(f"workspace_id = {sql_str_literal(cli_ctx.workspace_id)}")
97
98
  .to_list()
98
99
  )
99
-
100
+
100
101
  def format_dt(val: Any) -> str:
101
102
  if not val:
102
103
  return "never"
@@ -111,13 +112,13 @@ async def stats(
111
112
  total = len(records)
112
113
  accepted = sum(1 for r in records if r.get("status") == "accepted")
113
114
  proposed = sum(1 for r in records if r.get("status") == "proposed")
114
-
115
+
115
116
  valid_ats = [r.get("valid_at") for r in records if r.get("valid_at")]
116
117
  last_created = format_dt(max(valid_ats)) if valid_ats else "never"
117
-
118
+
118
119
  accepted_valid_ats = [r.get("valid_at") for r in records if r.get("valid_at") and r.get("status") == "accepted"]
119
120
  last_accepted = format_dt(max(accepted_valid_ats)) if accepted_valid_ats else "never"
120
-
121
+
121
122
  temporal_stats.update({
122
123
  "total": total,
123
124
  "accepted": accepted,
@@ -133,7 +134,7 @@ async def stats(
133
134
  if show_graph:
134
135
  try:
135
136
  graph_stats = {"entity_count": 0, "edge_count": 0, "predicates": {}, "languages": []}
136
-
137
+
137
138
  langs = []
138
139
  if await provider._connection.table_exists("code_chunks"):
139
140
  code_table = await provider._connection.get_table("code_chunks")
@@ -145,7 +146,7 @@ async def stats(
145
146
  .to_list()
146
147
  )
147
148
  langs = sorted(list({r["language"] for r in records if r.get("language")}))
148
-
149
+
149
150
  entity_count = 0
150
151
  if await provider._connection.table_exists("entities"):
151
152
  table = await provider._connection.get_table("entities")
@@ -155,7 +156,7 @@ async def stats(
155
156
  .to_list()
156
157
  )
157
158
  entity_count = len(records)
158
-
159
+
159
160
  edge_count = 0
160
161
  predicates = {}
161
162
  if await provider._connection.table_exists("triples"):
@@ -170,7 +171,7 @@ async def stats(
170
171
  p = r.get("predicate")
171
172
  if p:
172
173
  predicates[p] = predicates.get(p, 0) + 1
173
-
174
+
174
175
  graph_stats.update({
175
176
  "entity_count": entity_count,
176
177
  "edge_count": edge_count,
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  from pathlib import Path
7
+
7
8
  import typer
8
9
 
9
10
  from contextual.cli.core import register_command
@@ -31,7 +32,7 @@ async def workspace_list() -> None:
31
32
  for r in records:
32
33
  path_str = r.get("path") or ""
33
34
  ws_id = r.get("id") or ""
34
-
35
+
35
36
  # Attempt to load last_indexed_at from state.json on disk if the path exists
36
37
  last_indexed_at = "never"
37
38
  if path_str:
@@ -18,8 +18,8 @@ Design system:
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
- from collections.abc import Generator
22
21
  import contextlib
22
+ from collections.abc import Generator
23
23
  from dataclasses import dataclass
24
24
  from typing import Any
25
25
 
@@ -100,6 +100,7 @@ def print_index_start(path: str) -> None:
100
100
 
101
101
  import sys as _sys
102
102
 
103
+
103
104
  class IndexProgressHandle:
104
105
  """Lightweight stdout-based stage printer. No threads, no Rich.Progress.
105
106
 
@@ -134,7 +135,7 @@ class IndexProgressHandle:
134
135
  pass # no-op — Ollama style, no per-file updates
135
136
 
136
137
  def finish_chunking(self, chunk_count: int = 0) -> None:
137
- self._write(f"done\n")
138
+ self._write("done\n")
138
139
 
139
140
  def start_embedding(self, total_chunks: int = 0) -> None:
140
141
  self._write(f" Embedding + writing {self._DOTS} ")
@@ -370,7 +371,7 @@ def print_global_setup_complete(
370
371
  padding=(0, 1),
371
372
  )
372
373
  )
373
- console.print(f" [#6b6b6b]Next →[/#6b6b6b] [white]contextual fetch[/white]")
374
+ console.print(" [#6b6b6b]Next →[/#6b6b6b] [white]contextual fetch[/white]")
374
375
  console.print()
375
376
 
376
377
 
@@ -628,7 +629,7 @@ def print_fetch_complete(downloaded: list[str], already_present: list[str]) -> N
628
629
  padding=(0, 1),
629
630
  )
630
631
  )
631
- console.print(f" [#6b6b6b]Next →[/#6b6b6b] [white]contextual init .[/white]")
632
+ console.print(" [#6b6b6b]Next →[/#6b6b6b] [white]contextual init .[/white]")
632
633
  console.print()
633
634
 
634
635
 
@@ -660,8 +661,8 @@ def print_purge_complete(workspace_name: str) -> None:
660
661
  f"[white]{workspace_name}[/white] [#6b6b6b]fully deregistered[/#6b6b6b]"
661
662
  )
662
663
  console.print(
663
- f" [#6b6b6b]Run[/#6b6b6b] [white]contextual init .[/white] "
664
- f"[#6b6b6b]to reinitialise this workspace.[/#6b6b6b]"
664
+ " [#6b6b6b]Run[/#6b6b6b] [white]contextual init .[/white] "
665
+ "[#6b6b6b]to reinitialise this workspace.[/#6b6b6b]"
665
666
  )
666
667
  console.print()
667
668
 
@@ -702,7 +703,7 @@ def print_init_already_done(workspace_id: str, path: str) -> None:
702
703
  padding=(0, 2),
703
704
  )
704
705
  )
705
- console.print(f" [#6b6b6b]Next →[/#6b6b6b] [white]contextual index .[/white]")
706
+ console.print(" [#6b6b6b]Next →[/#6b6b6b] [white]contextual index .[/white]")
706
707
  console.print()
707
708
 
708
709
 
@@ -733,7 +734,7 @@ def print_init_complete(
733
734
  padding=(0, 1),
734
735
  )
735
736
  )
736
- console.print(f" [#6b6b6b]Next →[/#6b6b6b] [white]contextual index .[/white]")
737
+ console.print(" [#6b6b6b]Next →[/#6b6b6b] [white]contextual index .[/white]")
737
738
  console.print()
738
739
 
739
740
 
@@ -843,7 +844,7 @@ def print_auth_stub(command: str) -> None:
843
844
  f"[#6b6b6b]— coming in auth hardening session[/#6b6b6b]"
844
845
  )
845
846
  console.print(
846
- f" [#6b6b6b]Authentication via Clerk + Cryptolens will be "
847
- f"implemented pre-launch.[/#6b6b6b]"
847
+ " [#6b6b6b]Authentication via Clerk + Cryptolens will be "
848
+ "implemented pre-launch.[/#6b6b6b]"
848
849
  )
849
850
  console.print()
@@ -145,13 +145,22 @@ class GitIntegration:
145
145
  unstaged = [item.decode("utf-8", errors="replace") for item in status.unstaged]
146
146
  untracked = [item.decode("utf-8", errors="replace") for item in status.untracked]
147
147
 
148
+ seen: set[str] = set(added + modified + deleted)
148
149
  for relative_path in unstaged + untracked:
149
- if (
150
- relative_path not in added
151
- and relative_path not in modified
152
- and relative_path not in deleted
153
- ):
150
+ if relative_path in seen:
151
+ continue
152
+ full = Path(self.repo_path) / relative_path
153
+ if full.is_dir():
154
+ # dulwich reports untracked dirs as a single entry; expand to files.
155
+ for child in full.rglob("*"):
156
+ if child.is_file():
157
+ child_rel = str(child.relative_to(self.repo_path))
158
+ if child_rel not in seen:
159
+ modified.append(child_rel)
160
+ seen.add(child_rel)
161
+ else:
154
162
  modified.append(relative_path)
163
+ seen.add(relative_path)
155
164
 
156
165
  return ChangedFiles(added=added, modified=modified, deleted=deleted)
157
166
 
@@ -274,6 +283,7 @@ def get_git_integration(repo_path: Path | str) -> GitIntegration:
274
283
 
275
284
  import atexit
276
285
 
286
+
277
287
  def _cleanup_git_repos() -> None:
278
288
  for integration in list(_git_integration_cache.values()):
279
289
  integration.close()
@@ -17,9 +17,8 @@ process pool can be reintroduced behind the same interface if ever needed.
17
17
  from __future__ import annotations
18
18
 
19
19
  import asyncio
20
- from datetime import datetime, timezone
21
- import math
22
20
  from dataclasses import dataclass, field
21
+ from datetime import datetime
23
22
  from typing import Any
24
23
 
25
24
  import structlog
@@ -510,7 +509,6 @@ def _make_graph_result(content: str, query: str) -> Any:
510
509
 
511
510
 
512
511
  def _compute_staleness(node: dict | None = None, *, change_velocity: float | None = None, valid_at: datetime | None = None) -> float:
513
- from contextual.graph.staleness import compute_staleness
514
512
  if node is not None:
515
513
  change_velocity = node.get("change_velocity")
516
514
  valid_at = node.get("valid_at")
@@ -40,11 +40,11 @@ CONFIDENCE_FLOOR = 0.55
40
40
  # Avoids a 500 K-row LanceDB scan per incremental-indexing file event.
41
41
  # Entries are invalidated by invalidate_registry_cache() after a full re-index
42
42
  # and expire naturally after _REGISTRY_TTL_SECONDS of inactivity.
43
- _registry_cache: dict[str, tuple["FunctionRegistry", float]] = {}
43
+ _registry_cache: dict[str, tuple[FunctionRegistry, float]] = {}
44
44
  _REGISTRY_TTL_SECONDS: float = 120.0 # 2 minutes
45
45
 
46
46
 
47
- async def get_or_build_registry(connector: Any, workspace_id: str) -> "FunctionRegistry":
47
+ async def get_or_build_registry(connector: Any, workspace_id: str) -> FunctionRegistry:
48
48
  """Return a cached FunctionRegistry for workspace_id, rebuilding if stale.
49
49
 
50
50
  Thread-safe for a single async event loop (no lock needed — coroutines
@@ -16,8 +16,7 @@ Used in two places:
16
16
  from __future__ import annotations
17
17
 
18
18
  import math
19
- from datetime import datetime, timezone
20
-
19
+ from datetime import UTC, datetime
21
20
 
22
21
  # Staleness thresholds
23
22
  STALENESS_FRESH = 0.0 # definitely stable / recently updated
@@ -70,9 +69,9 @@ def compute_staleness(
70
69
  return 0.0
71
70
 
72
71
  # Ensure timezone-aware comparison
73
- now = datetime.now(timezone.utc)
72
+ now = datetime.now(UTC)
74
73
  if valid_at.tzinfo is None:
75
- valid_at = valid_at.replace(tzinfo=timezone.utc)
74
+ valid_at = valid_at.replace(tzinfo=UTC)
76
75
 
77
76
  days_since = max(0.0, (now - valid_at).total_seconds() / 86400.0)
78
77
  recency_decay = math.exp(-days_since / decay_constant)
@@ -15,13 +15,10 @@ Performance targets (Python BFS @ ~1M edges): depth-1 < 50ms, depth-5 < 200ms.
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
- import math
19
18
  from typing import Any
20
19
 
21
20
  import structlog
22
21
 
23
- from contextual.graph.staleness import compute_staleness
24
-
25
22
  logger = structlog.get_logger(__name__)
26
23
 
27
24
  # Only follow edges at/above this confidence by default (drops fuzzy edges).