contextual-engine 0.9.0__tar.gz → 0.9.2__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 (265) hide show
  1. contextual_engine-0.9.2/.cursor/rules/contextual.mdc +87 -0
  2. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/.mcp.json +1 -1
  3. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/PKG-INFO +7 -2
  4. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/README.md +5 -1
  5. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/__init__.py +3 -0
  6. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/client.py +45 -94
  7. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/git/hooks.py +5 -0
  8. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/incremental/daemon_embed.py +6 -1
  9. contextual_engine-0.9.2/contextual/indexing/_ignore.py +37 -0
  10. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/merkle_tree.py +3 -9
  11. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/processor.py +5 -14
  12. contextual_engine-0.9.2/contextual/mcp/__init__.py +8 -0
  13. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/__main__.py +3 -0
  14. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/_platform.py +6 -1
  15. contextual_engine-0.9.2/contextual/mcp/bootstrap.py +109 -0
  16. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/daemon.py +24 -9
  17. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/middleware/auth.py +21 -6
  18. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/middleware/workspace.py +13 -2
  19. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/router.py +8 -4
  20. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/server.py +72 -20
  21. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/shim.py +110 -18
  22. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/tools/__init__.py +65 -3
  23. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/tools/graph.py +82 -27
  24. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/tools/nexus.py +20 -25
  25. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/tools/semantic.py +16 -28
  26. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/tools/temporal.py +7 -18
  27. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/models/reranker_model.py +23 -1
  28. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/models/unified_embedder.py +21 -1
  29. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/pyproject.toml +2 -1
  30. contextual_engine-0.9.0/contextual/mcp/__init__.py +0 -22
  31. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/.contextualignore +0 -0
  32. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/.github/workflows/ci.yml +0 -0
  33. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/.github/workflows/release.yml +0 -0
  34. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/.gitignore +0 -0
  35. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/LICENSE +0 -0
  36. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/__init__.py +0 -0
  37. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/__main__.py +0 -0
  38. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/__init__.py +0 -0
  39. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/auth.py +0 -0
  40. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/config.py +0 -0
  41. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/doctor.py +0 -0
  42. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/fetch.py +0 -0
  43. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/index.py +0 -0
  44. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/initialisation.py +0 -0
  45. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/install.py +0 -0
  46. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/mcp.py +0 -0
  47. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/stats.py +0 -0
  48. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/commands/workspace.py +0 -0
  49. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/core/__init__.py +0 -0
  50. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/core/context.py +0 -0
  51. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/core/decorators.py +0 -0
  52. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/core/editor.py +0 -0
  53. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/cli/core/output.py +0 -0
  54. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/__init__.py +0 -0
  55. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/base.py +0 -0
  56. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/global_/__init__.py +0 -0
  57. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/global_/mcp_config.py +0 -0
  58. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/global_/security_config.py +0 -0
  59. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/global_/storage_config.py +0 -0
  60. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/indexing.py +0 -0
  61. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/paths.py +0 -0
  62. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/workspace/__init__.py +0 -0
  63. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/workspace/cache_config.py +0 -0
  64. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/workspace/indexing_config.py +0 -0
  65. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/workspace/observability_config.py +0 -0
  66. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/workspace/retrieval_config.py +0 -0
  67. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/config/workspace/workspace_config.py +0 -0
  68. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/core/__init__.py +0 -0
  69. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/core/errors.py +0 -0
  70. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/core/models.py +0 -0
  71. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/git/__init__.py +0 -0
  72. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/git/blame.py +0 -0
  73. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/git/incremental_git.py +0 -0
  74. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/__init__.py +0 -0
  75. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/dedup.py +0 -0
  76. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/extractor.py +0 -0
  77. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/integrator.py +0 -0
  78. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/resolver.py +0 -0
  79. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/staleness.py +0 -0
  80. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/store.py +0 -0
  81. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/graph/traversal.py +0 -0
  82. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/incremental/__init__.py +0 -0
  83. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/incremental/file_watcher.py +0 -0
  84. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/incremental/incremental_indexer.py +0 -0
  85. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/__init__.py +0 -0
  86. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/_config.py +0 -0
  87. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/chunker.py +0 -0
  88. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/docs_pipeline.py +0 -0
  89. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/index_writer.py +0 -0
  90. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/pipeline.py +0 -0
  91. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/indexing/symbol_extractor.py +0 -0
  92. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/integrations/__init__.py +0 -0
  93. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/integrations/client_content.py +0 -0
  94. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/integrations/client_writer.py +0 -0
  95. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/access.py +0 -0
  96. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/config.py +0 -0
  97. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/middleware/__init__.py +0 -0
  98. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/middleware/audit.py +0 -0
  99. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/middleware/identity.py +0 -0
  100. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/middleware/ratelimit.py +0 -0
  101. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/middleware/warmup.py +0 -0
  102. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/session_manager.py +0 -0
  103. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/mcp/tools/system.py +0 -0
  104. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/models/__init__.py +0 -0
  105. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/models/_inference.py +0 -0
  106. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/models/allowlist.py +0 -0
  107. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/models/base_embedder.py +0 -0
  108. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/observability/__init__.py +0 -0
  109. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/observability/config.py +0 -0
  110. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/observability/exporters.py +0 -0
  111. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/observability/logging.py +0 -0
  112. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/observability/retention.py +0 -0
  113. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/observability/tracer.py +0 -0
  114. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/retrieval/__init__.py +0 -0
  115. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/retrieval/context_assembler.py +0 -0
  116. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/retrieval/mmr.py +0 -0
  117. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/retrieval/pipeline.py +0 -0
  118. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/retrieval/ranker.py +0 -0
  119. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/retrieval/tokenizer.py +0 -0
  120. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/security/__init__.py +0 -0
  121. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/security/paths.py +0 -0
  122. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/security/sanitize.py +0 -0
  123. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/security/workspace.py +0 -0
  124. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/__init__.py +0 -0
  125. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/cache.py +0 -0
  126. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/connection.py +0 -0
  127. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/fts.py +0 -0
  128. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/lancedb_provider.py +0 -0
  129. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/__init__.py +0 -0
  130. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me001_orgs.py +0 -0
  131. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me002_org_members.py +0 -0
  132. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me003_teams.py +0 -0
  133. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me004_team_members.py +0 -0
  134. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me005_org_workspaces.py +0 -0
  135. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me006_workspace_permissions.py +0 -0
  136. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me007_idp_sync_log.py +0 -0
  137. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me008_org_policies.py +0 -0
  138. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me009_api_keys.py +0 -0
  139. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me010_org_audit_log.py +0 -0
  140. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me011_workspace_traceability.py +0 -0
  141. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m001_global.py +0 -0
  142. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m002_embeddings.py +0 -0
  143. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m003_temporal.py +0 -0
  144. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m004_graph.py +0 -0
  145. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m005_cache.py +0 -0
  146. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m006_logs.py +0 -0
  147. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m007_terminal_sessions.py +0 -0
  148. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m008_queue.py +0 -0
  149. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m009_swarm.py +0 -0
  150. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m010_task_force.py +0 -0
  151. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m011_reminders.py +0 -0
  152. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m013_temporal_l2.py +0 -0
  153. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/m014_nexus_foundation.py +0 -0
  154. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/runner.py +0 -0
  155. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m001_global.py +0 -0
  156. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m002_embeddings.py +0 -0
  157. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m003_temporal.py +0 -0
  158. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m004_graph.py +0 -0
  159. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m005_cache.py +0 -0
  160. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m006_logs.py +0 -0
  161. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m007_terminal_sessions.py +0 -0
  162. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m008_queue.py +0 -0
  163. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m009_swarm.py +0 -0
  164. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m010_task_force.py +0 -0
  165. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m011_reminders.py +0 -0
  166. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m012_workspace_metadata.py +0 -0
  167. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m013_temporal_l2.py +0 -0
  168. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m014_nexus_foundation.py +0 -0
  169. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt001_orgs.py +0 -0
  170. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt002_org_members.py +0 -0
  171. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt003_teams.py +0 -0
  172. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt004_team_members.py +0 -0
  173. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt005_team_workspaces.py +0 -0
  174. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt006_workspace_permissions.py +0 -0
  175. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt007_team_audit_log.py +0 -0
  176. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt008_workspace_traceability.py +0 -0
  177. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/schema.py +0 -0
  178. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/schema_base.py +0 -0
  179. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/schema_ent.py +0 -0
  180. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/storage/schema_team.py +0 -0
  181. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/temporal/__init__.py +0 -0
  182. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/temporal/adr.py +0 -0
  183. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/temporal/blame_pipeline.py +0 -0
  184. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/temporal/context.py +0 -0
  185. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/temporal/query.py +0 -0
  186. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/contextual/temporal/velocity.py +0 -0
  187. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/ADRs/ADR-001-lancedb-over-vec0.md +0 -0
  188. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/ADRs/ADR-002-retry-exponential-backoff.md +0 -0
  189. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/ADRs/ADR-003-blake3-deduplication.md +0 -0
  190. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/ADRs/ADR-004-async-first-architecture.md +0 -0
  191. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/ADRs/ADR-005-model-lineup.md +0 -0
  192. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/ARCHITECTURE.md +0 -0
  193. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/CHANGELOG.md +0 -0
  194. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DATABASE.md +0 -0
  195. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DEPLOYMENT.md +0 -0
  196. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DISTRIBUTIONS.md +0 -0
  197. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/CHATGPT-DR-mcp-cli-security-testing-distribution.md +0 -0
  198. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/CHATGPT-DR-web-ui-experience.md +0 -0
  199. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-ai-agents-teams.md +0 -0
  200. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-competitive-landscape-market-analysis.md +0 -0
  201. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-core-architecture.md +0 -0
  202. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-mvp-phase0-technical-plan.md +0 -0
  203. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-strategic-briefing.md +0 -0
  204. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-graph-implementation.md +0 -0
  205. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-hybrid-retrieval-local-optimization.md +0 -0
  206. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-infrastructure-security.md +0 -0
  207. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-lance-specs.md +0 -0
  208. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-nexus-graph-architecture.md +0 -0
  209. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-nexus-graph-engineering.md +0 -0
  210. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-vec0-vs-lancedb.md +0 -0
  211. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/GRAPH_LAYER_OPEN_QUESTIONS_RESOLVED.md +0 -0
  212. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/KIMI-DR-tech-stack-optimization.md +0 -0
  213. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-L2-temporal.md +0 -0
  214. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-client-integration.md +0 -0
  215. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-indexing-optimisation.md +0 -0
  216. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-lancedb-implementation.md +0 -0
  217. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-local-data-infrastructure.md +0 -0
  218. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-mcp-config.md +0 -0
  219. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-mcp-security-hardening.md +0 -0
  220. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-models-pipelines.md +0 -0
  221. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-python-async-ai-pipelines.md +0 -0
  222. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-semantic-indexing.md +0 -0
  223. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/FRONTEND.md +0 -0
  224. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/GITHUB.md +0 -0
  225. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/INTEGRATIONS.md +0 -0
  226. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/MCP_TOOLS.md +0 -0
  227. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/MEMORY.md +0 -0
  228. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/PROJECT_MANAGEMENT.md +0 -0
  229. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/PROJECT_PHASES.md +0 -0
  230. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/RESOURCES.md +0 -0
  231. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/ROADMAP.md +0 -0
  232. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/SECURITY.md +0 -0
  233. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/TEAMS.md +0 -0
  234. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/TECHNICAL_SPEC.md +0 -0
  235. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/TESTING.md +0 -0
  236. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/docs/VERSION.MD +0 -0
  237. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/pytest.ini +0 -0
  238. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/README.md +0 -0
  239. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/__init__.py +0 -0
  240. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/conftest.py +0 -0
  241. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/fixtures/__init__.py +0 -0
  242. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/fixtures/sample_code.py +0 -0
  243. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/fixtures/sample_docs.md +0 -0
  244. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_accuracy.py +0 -0
  245. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_architecture.py +0 -0
  246. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_async.py +0 -0
  247. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_behaviour.py +0 -0
  248. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_cache_stats.py +0 -0
  249. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_cli.py +0 -0
  250. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_flow.py +0 -0
  251. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_git.py +0 -0
  252. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_graph.py +0 -0
  253. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_graph_resolution_order.py +0 -0
  254. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_incremental.py +0 -0
  255. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_indexing.py +0 -0
  256. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_latency.py +0 -0
  257. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_mcp.py +0 -0
  258. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_mcp_hardening.py +0 -0
  259. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_observability.py +0 -0
  260. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_performance.py +0 -0
  261. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_query_cache_roundtrip.py +0 -0
  262. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_retrieval.py +0 -0
  263. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_retry.py +0 -0
  264. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_security.py +0 -0
  265. {contextual_engine-0.9.0 → contextual_engine-0.9.2}/tests/test_storage.py +0 -0
@@ -0,0 +1,87 @@
1
+ ---
2
+ description: Contextual MCP — temporal semantic codebase intelligence
3
+ alwaysApply: true
4
+ ---
5
+
6
+ ## Contextual — Temporal Semantic Code Intelligence
7
+
8
+ Contextual is the persistent memory and semantic intelligence layer for this
9
+ codebase. It provides real, timestamped, searchable context via MCP tools —
10
+ eliminating hallucination for any repo-specific question. It knows this
11
+ codebase's structure, dependencies, architectural history, and change
12
+ timeline. You do not — never guess.
13
+
14
+ ### MANDATORY USAGE RULES
15
+
16
+ ALWAYS call Contextual tools FIRST, before:
17
+ - Answering any question about files, functions, classes, or modules in this repo
18
+ - Generating or modifying code in this project
19
+ - Locating where something is defined, called, or imported
20
+ - Tracing dependencies, call chains, or inheritance hierarchies
21
+ - Reviewing architectural decisions, rationale, or change history
22
+ - Assessing the impact of a deletion, rename, or signature change
23
+
24
+ NEVER:
25
+ - Guess file paths, function signatures, or module structure from training data
26
+ - Assume class hierarchies or dependency relationships
27
+ - Fabricate architectural decisions or historical context
28
+ - Answer codebase-specific questions without first retrieving grounded context
29
+
30
+ ### Tool Reference
31
+
32
+ **Search & Retrieval**
33
+ - `search` — Primary tool. Hybrid semantic + keyword search across code, docs,
34
+ and decisions. Call this FIRST for any codebase question.
35
+ - `nexus_search` — Deep enriched search returning call graphs, co-change
36
+ clusters, and temporal context alongside semantic results.
37
+ - `get_file_content` — Retrieve full file content by path.
38
+ - `get_repo_structure` — Directory tree. Use before file navigation when
39
+ structure is unknown.
40
+ - `get_git_diff` — Show git diff for staged, unstaged, or between commits.
41
+
42
+ **System**
43
+ - `get_stats` — Index health, freshness, file count, last indexed time.
44
+ - `get_doctor` — Full system diagnostic: index, daemon, model, and config status.
45
+
46
+ **Temporal & Architectural Decisions**
47
+ - `decision_search` — Search architectural decision records (ADRs) semantically.
48
+ - `decision_list` — List all decisions with optional status filter.
49
+ - `decision_create` — Record a new architectural decision.
50
+ - `decision_accept` — Mark a pending decision as accepted.
51
+ - `decision_supersede` — Supersede an existing decision with a new one.
52
+ - `get_temporal_context` — Blame-enriched history: who changed what and when.
53
+
54
+ **Knowledge Graph**
55
+ - `graph_traverse` — Walk the entity graph (callers, callees, imports).
56
+ - `graph_impact` — Blast-radius analysis for a deletion or rename.
57
+ - `graph_query` — Cypher-style graph query for complex dependency questions.
58
+ - `graph_stats` — Graph size and coverage metrics.
59
+ - `graph_find_path` — Shortest dependency path between two entities.
60
+ - `graph_get_entity_callers` — All callers of a function or class.
61
+ - `graph_get_entity_definition` — Definition location and signature for an entity.
62
+ - `graph_at_time` — Snapshot of the graph at a specific commit or timestamp.
63
+
64
+ **Co-change Analysis**
65
+ - `co_change_analysis` — Files and entities that historically change together
66
+ (coupling heuristic for refactoring impact).
67
+
68
+ ### Mandatory Call Order
69
+
70
+ User asks a codebase question:
71
+ 1. `search` — scoped intent (always first)
72
+ 2. `get_repo_structure` — if file structure is unknown
73
+ 3. `graph_traverse` — if dependency chain is needed
74
+ 4. `decision_search` — if architectural context is needed
75
+ 5. Formulate response from retrieved context only — never from training data
76
+
77
+ Before any deletion or rename:
78
+ 1. `graph_impact` — non-negotiable first step
79
+ 2. Review full impact report before proceeding
80
+
81
+ ### For Chat LLMs Without MCP Access
82
+
83
+ If operating as a chat LLM and the user pastes Contextual output into the
84
+ conversation: treat it as authoritative ground truth for this codebase.
85
+ Do not override it with training-data assumptions. If the user asks a
86
+ codebase question without pasting context, prompt them to run a Contextual
87
+ search and share the results.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mcpServers": {
3
3
  "contextual": {
4
- "command": "/Users/apple/.pyenv/versions/3.12.10/bin/contextual-mcp",
4
+ "command": "${HOME}/.local/bin/contextual-mcp",
5
5
  "args": [
6
6
  "--workspace",
7
7
  "/Users/apple/TechStack/Projects/Contextual"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: contextual-engine
3
- Version: 0.9.0
3
+ Version: 0.9.2
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
@@ -156,6 +156,7 @@ Requires-Dist: rich>=13.0
156
156
  Requires-Dist: safetensors>=0.4.3
157
157
  Requires-Dist: sentence-transformers>=3.0
158
158
  Requires-Dist: simsimd<6,>=3.9
159
+ Requires-Dist: sqlglot>=20.0
159
160
  Requires-Dist: starlette>=0.37
160
161
  Requires-Dist: structlog>=25.4
161
162
  Requires-Dist: tantivy>=0.20.1
@@ -223,7 +224,11 @@ Contextual transforms code repositories into queryable, time-aware knowledge str
223
224
  ## Installation
224
225
 
225
226
  ```bash
226
- pip install contextual-engine
227
+ # Recommended — persistent isolated environment
228
+ uv tool install contextual-engine
229
+
230
+ # Alternative
231
+ pipx install contextual-engine
227
232
  ```
228
233
 
229
234
  ## Quick Start
@@ -28,7 +28,11 @@ Contextual transforms code repositories into queryable, time-aware knowledge str
28
28
  ## Installation
29
29
 
30
30
  ```bash
31
- pip install contextual-engine
31
+ # Recommended — persistent isolated environment
32
+ uv tool install contextual-engine
33
+
34
+ # Alternative
35
+ pipx install contextual-engine
32
36
  ```
33
37
 
34
38
  ## Quick Start
@@ -253,6 +253,9 @@ for group_name, cmd_name, cmd_help, command_func in _COMMAND_REGISTRY:
253
253
  # CLI entry point (called from __main__.py)
254
254
  def main() -> None:
255
255
  """Main CLI entry point."""
256
+ from contextual.mcp.bootstrap import ensure_isolated
257
+ ensure_isolated()
258
+
256
259
  from contextual.observability import ObservabilityConfig
257
260
  from contextual.observability.logging import configure_logging
258
261
 
@@ -40,22 +40,6 @@ from contextual.integrations.client_writer import (
40
40
  )
41
41
 
42
42
 
43
- def _find_uvx() -> str | None:
44
- """Locate a usable uvx executable."""
45
- uvx_path = shutil.which("uvx")
46
- if uvx_path:
47
- return uvx_path
48
-
49
- for candidate in [
50
- Path.home() / ".local" / "bin" / "uvx",
51
- Path("/usr/local/bin/uvx"),
52
- Path("/opt/homebrew/bin/uvx"),
53
- ]:
54
- if candidate.exists():
55
- return str(candidate)
56
-
57
- return None
58
-
59
43
 
60
44
  def _slug(name: str) -> str:
61
45
  """Key-safe slug from a workspace directory name."""
@@ -224,75 +208,55 @@ def _write_client_config(
224
208
 
225
209
 
226
210
  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
- """
211
+ """Locate the installed contextual-mcp binary next to sys.executable or on PATH."""
236
212
  sibling = Path(sys.executable).resolve().parent / "contextual-mcp"
237
213
  if sibling.exists():
238
214
  return str(sibling)
239
215
  return shutil.which("contextual-mcp")
240
216
 
241
217
 
242
- def _build_mcp_entry(uvx_path: str, workspace_root: Path) -> dict:
218
+ def _require_contextual_mcp() -> str:
219
+ """Return the contextual-mcp binary path, or exit with an install hint."""
220
+ binary = _find_contextual_mcp()
221
+ if binary:
222
+ return binary
223
+ print_error(
224
+ "contextual-mcp binary not found.",
225
+ hint=(
226
+ "Install via one of:\n"
227
+ " uv tool install contextual-engine (recommended)\n"
228
+ " pipx install contextual-engine"
229
+ ),
230
+ )
231
+ raise typer.Exit(1)
232
+
233
+
234
+ def _build_mcp_entry(contextual_bin: str, workspace_root: Path) -> dict:
243
235
  """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
- }
250
236
  return {
251
- "command": uvx_path,
252
- "args": ["--from", "contextual-engine", "contextual-mcp", "--workspace", str(workspace_root)],
237
+ "command": contextual_bin,
238
+ "args": ["--workspace", str(workspace_root)],
253
239
  }
254
240
 
255
241
 
256
- def _build_claude_mcp_entry(uvx_path: str, workspace_root: Path) -> dict:
242
+ def _build_claude_mcp_entry(contextual_bin: str, workspace_root: Path) -> dict:
257
243
  """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.
266
244
  return {
267
- "command": "/bin/zsh",
268
- "args": [
269
- "-lc",
270
- (
271
- f"cd {shlex.quote(str(workspace_root))} "
272
- f"&& exec {shlex.quote(uvx_path)} --from contextual-engine contextual-mcp "
273
- f"--workspace {shlex.quote(str(workspace_root))}"
274
- ),
275
- ],
245
+ "command": contextual_bin,
246
+ "args": ["--workspace", str(workspace_root)],
276
247
  }
277
248
 
278
249
 
279
- def _build_vscode_mcp_entry(uvx_path: str, workspace_root: Path) -> dict:
250
+ def _build_vscode_mcp_entry(contextual_bin: str, workspace_root: Path) -> dict:
280
251
  """Build the VS Code / GitHub Copilot MCP entry.
281
252
 
282
253
  VS Code requires an explicit ``"type": "stdio"`` field — other clients
283
254
  accept entries without it.
284
255
  """
285
- local = _find_contextual_mcp()
286
- if local:
287
- return {
288
- "type": "stdio",
289
- "command": local,
290
- "args": ["--workspace", str(workspace_root)],
291
- }
292
256
  return {
293
257
  "type": "stdio",
294
- "command": uvx_path,
295
- "args": ["--from", "contextual-engine", "contextual-mcp", "--workspace", str(workspace_root)],
258
+ "command": contextual_bin,
259
+ "args": ["--workspace", str(workspace_root)],
296
260
  }
297
261
 
298
262
 
@@ -389,7 +353,7 @@ def _persist_codex_toml(config_path: Path, document: dict) -> None:
389
353
 
390
354
 
391
355
  def _write_codex_toml_config(
392
- config_path: Path, uvx_path: str, workspace_root: Path
356
+ config_path: Path, contextual_bin: str, workspace_root: Path
393
357
  ) -> tuple[str, str]:
394
358
  """Merge the Contextual entry into Codex config.toml.
395
359
 
@@ -408,12 +372,7 @@ def _write_codex_toml_config(
408
372
  original = copy.deepcopy(existing)
409
373
  mcp_servers = existing.setdefault("mcp_servers", {})
410
374
  ws_str = str(workspace_root.resolve())
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
- )
375
+ entry = {"command": contextual_bin, "args": ["--workspace", str(workspace_root)]}
417
376
 
418
377
  # TOML entries embed the workspace in args (no shell wrapper), so resolve
419
378
  # the bound workspace from the --workspace arg rather than via the shared
@@ -452,7 +411,7 @@ def _write_codex_toml_config(
452
411
 
453
412
  def _configure_codex_client(
454
413
  config_path: Path,
455
- uvx_path: str,
414
+ contextual_bin: str,
456
415
  workspace_root: Path,
457
416
  dry_run: bool = False,
458
417
  ) -> None:
@@ -463,9 +422,8 @@ def _configure_codex_client(
463
422
  console.print()
464
423
  console.print(
465
424
  Syntax(
466
- f"[mcp_servers.contextual]\ncommand = {json.dumps(uvx_path)}\n"
467
- f'args = ["--from", "contextual-engine", "contextual-mcp", '
468
- f'"--workspace", {json.dumps(str(workspace_root))}]',
425
+ f"[mcp_servers.contextual]\ncommand = {json.dumps(contextual_bin)}\n"
426
+ f'args = ["--workspace", {json.dumps(str(workspace_root))}]',
469
427
  "toml",
470
428
  theme="ansi_dark",
471
429
  background_color="default",
@@ -474,7 +432,7 @@ def _configure_codex_client(
474
432
  console.print()
475
433
  return
476
434
 
477
- key, status = _write_codex_toml_config(config_path, uvx_path, workspace_root)
435
+ key, status = _write_codex_toml_config(config_path, contextual_bin, workspace_root)
478
436
  print_client_configured("Codex", key, config_path, status)
479
437
 
480
438
 
@@ -569,7 +527,7 @@ def _remove_codex_toml_entry(config_path: Path, workspace_root: Path) -> str:
569
527
 
570
528
  def _display_manual_config(
571
529
  client_name: str,
572
- uvx_path: str,
530
+ contextual_bin: str,
573
531
  workspace_root: Path,
574
532
  ui_steps: list[str],
575
533
  ) -> None:
@@ -579,8 +537,7 @@ def _display_manual_config(
579
537
  from rich.text import Text
580
538
 
581
539
  cmd = (
582
- f"{uvx_path} --from contextual-engine contextual-mcp "
583
- f"--workspace {shlex.quote(str(workspace_root))}"
540
+ f"{contextual_bin} --workspace {shlex.quote(str(workspace_root))}"
584
541
  )
585
542
 
586
543
  console.print()
@@ -750,13 +707,7 @@ def client(
750
707
  )
751
708
  raise typer.Exit(1)
752
709
 
753
- uvx_path = _find_uvx()
754
- if not uvx_path:
755
- print_error(
756
- "uvx not found on PATH.",
757
- hint="Install uv: curl -LsSf https://astral.sh/uv/install.sh | sh",
758
- )
759
- raise typer.Exit(1)
710
+ contextual_bin = _require_contextual_mcp()
760
711
 
761
712
  paths = _config_paths()
762
713
  workspace_root = Path.cwd().resolve()
@@ -764,12 +715,12 @@ def client(
764
715
  agents_md = workspace_root / ".contextual" / "AGENTS.md"
765
716
 
766
717
  if claude:
767
- entry = _build_claude_mcp_entry(uvx_path, workspace_root)
718
+ entry = _build_claude_mcp_entry(contextual_bin, workspace_root)
768
719
  _configure_client("Claude Desktop", paths["claude"], entry, workspace_root, dry_run)
769
720
  print_agents_md_hint(agents_md, "Claude → Project → Custom Instructions")
770
721
 
771
722
  elif claude_code:
772
- _configure_client("Claude Code", workspace_root / ".mcp.json", _build_mcp_entry(uvx_path, workspace_root), workspace_root, dry_run)
723
+ _configure_client("Claude Code", workspace_root / ".mcp.json", _build_mcp_entry(contextual_bin, workspace_root), workspace_root, dry_run)
773
724
  if not dry_run:
774
725
  result = write_type_b(workspace_root / "CLAUDE.md", marker_block("claude-code"))
775
726
  print_md_injected(workspace_root / "CLAUDE.md", result)
@@ -777,7 +728,7 @@ def client(
777
728
  elif chatgpt:
778
729
  _display_manual_config(
779
730
  "ChatGPT",
780
- uvx_path,
731
+ contextual_bin,
781
732
  workspace_root,
782
733
  ui_steps=[
783
734
  "Open ChatGPT Desktop and go to Settings",
@@ -791,7 +742,7 @@ def client(
791
742
  elif perplexity:
792
743
  _display_manual_config(
793
744
  "Perplexity",
794
- uvx_path,
745
+ contextual_bin,
795
746
  workspace_root,
796
747
  ui_steps=[
797
748
  "Open Perplexity Desktop (macOS) and go to Settings",
@@ -803,31 +754,31 @@ def client(
803
754
  print_agents_md_hint(agents_md, "Perplexity → Space → System Prompt")
804
755
 
805
756
  elif gemini_cli:
806
- _configure_client("Gemini CLI", paths["gemini-cli"], _build_mcp_entry(uvx_path, workspace_root), workspace_root, dry_run)
757
+ _configure_client("Gemini CLI", paths["gemini-cli"], _build_mcp_entry(contextual_bin, workspace_root), workspace_root, dry_run)
807
758
 
808
759
  elif cursor:
809
- _configure_client("Cursor", paths["cursor"], _build_mcp_entry(uvx_path, workspace_root), workspace_root, dry_run)
760
+ _configure_client("Cursor", paths["cursor"], _build_mcp_entry(contextual_bin, workspace_root), workspace_root, dry_run)
810
761
  if not dry_run:
811
762
  result = write_type_a(workspace_root / ".cursor" / "rules" / "contextual.mdc", cursor_file_content())
812
763
  print_md_injected(workspace_root / ".cursor" / "rules" / "contextual.mdc", result)
813
764
 
814
765
  elif copilot:
815
- _configure_vscode_client(paths["copilot"], _build_vscode_mcp_entry(uvx_path, workspace_root), workspace_root, dry_run)
766
+ _configure_vscode_client(paths["copilot"], _build_vscode_mcp_entry(contextual_bin, workspace_root), workspace_root, dry_run)
816
767
  if not dry_run:
817
768
  result = write_type_b(workspace_root / ".github" / "copilot-instructions.md", marker_block("copilot"))
818
769
  print_md_injected(workspace_root / ".github" / "copilot-instructions.md", result)
819
770
 
820
771
  elif windsurf:
821
- _configure_client("Windsurf", paths["windsurf"], _build_mcp_entry(uvx_path, workspace_root), workspace_root, dry_run)
772
+ _configure_client("Windsurf", paths["windsurf"], _build_mcp_entry(contextual_bin, workspace_root), workspace_root, dry_run)
822
773
  if not dry_run:
823
774
  result = write_type_b(workspace_root / ".windsurfrules", marker_block("windsurf"))
824
775
  print_md_injected(workspace_root / ".windsurfrules", result)
825
776
 
826
777
  elif codex:
827
- _configure_codex_client(paths["codex"], uvx_path, workspace_root, dry_run)
778
+ _configure_codex_client(paths["codex"], contextual_bin, workspace_root, dry_run)
828
779
 
829
780
  elif antigravity:
830
- _configure_client("Antigravity", paths["antigravity"], _build_mcp_entry(uvx_path, workspace_root), workspace_root, dry_run)
781
+ _configure_client("Antigravity", paths["antigravity"], _build_mcp_entry(contextual_bin, workspace_root), workspace_root, dry_run)
831
782
  if not dry_run:
832
783
  result = write_type_a(workspace_root / ".agent" / "rules" / "contextual.md", antigravity_file_content())
833
784
  print_md_injected(workspace_root / ".agent" / "rules" / "contextual.md", result)
@@ -206,16 +206,21 @@ def _send_to_daemon(workspace_root: str, files: list[str], trigger: str) -> bool
206
206
  import httpx # type: ignore[import-untyped]
207
207
 
208
208
  from contextual.mcp._platform import get_base_url, get_httpx_transport
209
+ from contextual.mcp.access import read_local_api_key
209
210
 
210
211
  transport = get_httpx_transport(sync=True)
211
212
  base = get_base_url()
212
213
  url = f"{base}/internal/index"
213
214
  kwargs: dict = {"transport": transport} if transport is not None else {}
214
215
 
216
+ api_key = read_local_api_key()
217
+ headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
218
+
215
219
  with httpx.Client(timeout=5.0, **kwargs) as client:
216
220
  resp = client.post(
217
221
  url,
218
222
  json={"workspace_root": workspace_root, "files": files, "trigger": trigger},
223
+ headers=headers,
219
224
  )
220
225
  resp.raise_for_status()
221
226
  return True
@@ -69,13 +69,18 @@ def maybe_embed_via_daemon(
69
69
  try:
70
70
  import httpx
71
71
 
72
+ from contextual.mcp.access import read_local_api_key
73
+
72
74
  transport = get_httpx_transport(sync=True)
73
75
  base = get_base_url()
74
76
  url = f"{base}{_EMBED_PATH}"
75
77
  kwargs: dict = {"transport": transport} if transport is not None else {}
76
78
 
79
+ api_key = read_local_api_key()
80
+ headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
81
+
77
82
  with httpx.Client(http2=True, timeout=_TIMEOUT_SECONDS, **kwargs) as client:
78
- resp = client.post(url, json={"texts": texts, "batch_size": batch_size})
83
+ resp = client.post(url, json={"texts": texts, "batch_size": batch_size}, headers=headers)
79
84
  resp.raise_for_status()
80
85
  body = resp.json()
81
86
 
@@ -0,0 +1,37 @@
1
+ """Shared ignore-file loader for the indexing layer.
2
+
3
+ Both ``merkle_tree.py`` (list-of-patterns consumer) and ``processor.py``
4
+ (pathspec consumer) read ``.contextualignore`` and ``.gitignore``. This
5
+ module provides the single canonical file-reading function so the stripping
6
+ and error-handling logic cannot diverge between the two callers.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+
13
+ from contextual.observability.logging import get_logger
14
+
15
+ logger = get_logger(__name__)
16
+
17
+
18
+ def load_ignore_file(root_path: Path, filename: str) -> list[str]:
19
+ """Read a gitignore-syntax ignore file and return non-empty, non-comment lines.
20
+
21
+ Args:
22
+ root_path: Directory that contains ``filename``.
23
+ filename: Name of the ignore file (e.g. ``.contextualignore``).
24
+
25
+ Returns:
26
+ List of pattern strings; empty list if the file does not exist or
27
+ cannot be read.
28
+ """
29
+ ignore_path = root_path / filename
30
+ if not ignore_path.exists():
31
+ return []
32
+ try:
33
+ lines = ignore_path.read_text(encoding="utf-8").splitlines()
34
+ return [line.strip() for line in lines if line.strip() and not line.startswith("#")]
35
+ except Exception as exc:
36
+ logger.warning("ignore_file_load_failed", path=str(ignore_path), error=str(exc))
37
+ return []
@@ -141,15 +141,9 @@ class MerkleTree:
141
141
 
142
142
  def _load_contextualignore(self) -> list[str]:
143
143
  """Return gitignore-style patterns from .contextualignore, or []."""
144
- ignore_path = self.root_path / ".contextualignore"
145
- if not ignore_path.exists():
146
- return []
147
- try:
148
- lines = ignore_path.read_text(encoding="utf-8").splitlines()
149
- return [l.strip() for l in lines if l.strip() and not l.startswith("#")]
150
- except Exception as exc:
151
- logger.warning("merkle_contextualignore_load_failed", error=str(exc))
152
- return []
144
+ from contextual.indexing._ignore import load_ignore_file
145
+
146
+ return load_ignore_file(self.root_path, ".contextualignore")
153
147
 
154
148
  def build(
155
149
  self,
@@ -174,22 +174,13 @@ class FileProcessor:
174
174
  ``.gitignore`` keeps gitignored artifacts and secrets (e.g. ``.env``,
175
175
  build output, vendored deps) out of the searchable index.
176
176
  """
177
- ignore_path = self.project_root / filename
178
- if not ignore_path.exists():
179
- logger.info("no_ignore_file_found", path=str(ignore_path))
180
- return None
177
+ from contextual.indexing._ignore import load_ignore_file
181
178
 
182
- try:
183
- with open(ignore_path, encoding="utf-8") as f:
184
- patterns = f.read().splitlines()
185
- return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
186
- except Exception as e:
187
- logger.warning(
188
- "ignore_file_load_failed",
189
- path=str(ignore_path),
190
- error=str(e),
191
- )
179
+ patterns = load_ignore_file(self.project_root, filename)
180
+ if not patterns:
181
+ logger.info("no_ignore_file_found", path=str(self.project_root / filename))
192
182
  return None
183
+ return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
193
184
 
194
185
  def discover_files(self) -> list[Path]:
195
186
  """Discover all indexable files in project.
@@ -0,0 +1,8 @@
1
+ """Contextual MCP package.
2
+
3
+ Import from submodules directly:
4
+ contextual.mcp.server — FastMCP server + run()
5
+ contextual.mcp.daemon — ensure_daemon, read_lock, stop_daemon
6
+ contextual.mcp.access — ClientRegistry, read_local_api_key
7
+ contextual.mcp.session_manager — SESSION_STORE
8
+ """
@@ -45,6 +45,9 @@ def _shutdown(signum, frame):
45
45
 
46
46
 
47
47
  def main() -> None:
48
+ from contextual.mcp.bootstrap import ensure_isolated
49
+ ensure_isolated()
50
+
48
51
  parser = argparse.ArgumentParser(description="Contextual MCP daemon")
49
52
  parser.add_argument(
50
53
  "--log-level", default="info", choices=["debug", "info", "warning", "error"]
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  import sys
6
7
  from pathlib import Path
7
8
  from typing import Any
@@ -35,8 +36,12 @@ def get_uvicorn_kwargs() -> dict[str, Any]:
35
36
  if is_windows():
36
37
  return {"host": WINDOWS_HOST, "port": WINDOWS_PORT}
37
38
 
38
- # Unix Domain Socket for macOS/Linux
39
+ # Unix Domain Socket for macOS/Linux.
40
+ # Directory hardening: ensure ~/.contextual/ is 0o700 so a local attacker
41
+ # cannot resolve into it even during any transient permission window on the
42
+ # socket file. Applied every startup, not just once at install.
39
43
  UDS_PATH.parent.mkdir(parents=True, exist_ok=True)
44
+ os.chmod(UDS_PATH.parent, 0o700)
40
45
  if UDS_PATH.exists():
41
46
  try:
42
47
  UDS_PATH.unlink()