contextual-engine 0.9.1__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.
- contextual_engine-0.9.2/.cursor/rules/contextual.mdc +87 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/.mcp.json +1 -1
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/PKG-INFO +7 -2
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/README.md +5 -1
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/__init__.py +3 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/client.py +45 -94
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/git/hooks.py +5 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/incremental/daemon_embed.py +6 -1
- contextual_engine-0.9.2/contextual/indexing/_ignore.py +37 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/merkle_tree.py +3 -9
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/processor.py +5 -14
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/__main__.py +3 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/_platform.py +6 -1
- contextual_engine-0.9.2/contextual/mcp/bootstrap.py +109 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/daemon.py +24 -9
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/middleware/auth.py +21 -6
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/middleware/workspace.py +13 -2
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/router.py +8 -4
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/server.py +72 -20
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/shim.py +110 -18
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/tools/__init__.py +65 -3
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/tools/graph.py +82 -27
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/tools/nexus.py +20 -25
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/tools/semantic.py +16 -28
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/tools/temporal.py +7 -18
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/models/reranker_model.py +23 -1
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/models/unified_embedder.py +21 -1
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/pyproject.toml +2 -1
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/.contextualignore +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/.github/workflows/ci.yml +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/.github/workflows/release.yml +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/.gitignore +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/LICENSE +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/__main__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/auth.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/doctor.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/fetch.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/index.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/initialisation.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/install.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/mcp.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/stats.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/commands/workspace.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/core/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/core/context.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/core/decorators.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/core/editor.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/cli/core/output.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/base.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/global_/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/global_/mcp_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/global_/security_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/global_/storage_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/indexing.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/paths.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/workspace/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/workspace/cache_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/workspace/indexing_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/workspace/observability_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/workspace/retrieval_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/config/workspace/workspace_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/core/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/core/errors.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/core/models.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/git/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/git/blame.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/git/incremental_git.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/dedup.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/extractor.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/integrator.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/resolver.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/staleness.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/store.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/graph/traversal.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/incremental/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/incremental/file_watcher.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/incremental/incremental_indexer.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/_config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/chunker.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/docs_pipeline.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/index_writer.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/pipeline.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/indexing/symbol_extractor.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/integrations/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/integrations/client_content.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/integrations/client_writer.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/access.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/middleware/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/middleware/audit.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/middleware/identity.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/middleware/ratelimit.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/middleware/warmup.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/session_manager.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/mcp/tools/system.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/models/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/models/_inference.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/models/allowlist.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/models/base_embedder.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/observability/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/observability/config.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/observability/exporters.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/observability/logging.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/observability/retention.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/observability/tracer.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/retrieval/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/retrieval/context_assembler.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/retrieval/mmr.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/retrieval/pipeline.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/retrieval/ranker.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/retrieval/tokenizer.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/security/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/security/paths.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/security/sanitize.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/security/workspace.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/cache.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/connection.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/fts.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/lancedb_provider.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me001_orgs.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me002_org_members.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me003_teams.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me004_team_members.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me005_org_workspaces.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me006_workspace_permissions.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me007_idp_sync_log.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me008_org_policies.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me009_api_keys.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me010_org_audit_log.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/enterprise/me011_workspace_traceability.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m001_global.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m002_embeddings.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m003_temporal.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m004_graph.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m005_cache.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m006_logs.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m007_terminal_sessions.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m008_queue.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m009_swarm.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m010_task_force.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m011_reminders.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m013_temporal_l2.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/m014_nexus_foundation.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/runner.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m001_global.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m002_embeddings.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m003_temporal.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m004_graph.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m005_cache.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m006_logs.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m007_terminal_sessions.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m008_queue.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m009_swarm.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m010_task_force.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m011_reminders.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m012_workspace_metadata.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m013_temporal_l2.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/solo/m014_nexus_foundation.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt001_orgs.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt002_org_members.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt003_teams.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt004_team_members.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt005_team_workspaces.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt006_workspace_permissions.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt007_team_audit_log.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/migrations/teams/mt008_workspace_traceability.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/schema.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/schema_base.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/schema_ent.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/storage/schema_team.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/temporal/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/temporal/adr.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/temporal/blame_pipeline.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/temporal/context.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/temporal/query.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/contextual/temporal/velocity.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/ADRs/ADR-001-lancedb-over-vec0.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/ADRs/ADR-002-retry-exponential-backoff.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/ADRs/ADR-003-blake3-deduplication.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/ADRs/ADR-004-async-first-architecture.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/ADRs/ADR-005-model-lineup.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/ARCHITECTURE.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/CHANGELOG.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DATABASE.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DEPLOYMENT.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DISTRIBUTIONS.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/CHATGPT-DR-mcp-cli-security-testing-distribution.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/CHATGPT-DR-web-ui-experience.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-ai-agents-teams.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-competitive-landscape-market-analysis.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-core-architecture.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-mvp-phase0-technical-plan.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/CLAUDE-DR-strategic-briefing.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-graph-implementation.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-hybrid-retrieval-local-optimization.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-infrastructure-security.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-lance-specs.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-nexus-graph-architecture.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-nexus-graph-engineering.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GEMINI-DR-vec0-vs-lancedb.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/GRAPH_LAYER_OPEN_QUESTIONS_RESOLVED.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/KIMI-DR-tech-stack-optimization.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-L2-temporal.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-client-integration.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-indexing-optimisation.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-lancedb-implementation.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-local-data-infrastructure.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-mcp-config.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-mcp-security-hardening.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-models-pipelines.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-python-async-ai-pipelines.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/DRs/PERPLEXITY-DR-semantic-indexing.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/FRONTEND.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/GITHUB.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/INTEGRATIONS.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/MCP_TOOLS.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/MEMORY.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/PROJECT_MANAGEMENT.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/PROJECT_PHASES.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/RESOURCES.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/ROADMAP.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/SECURITY.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/TEAMS.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/TECHNICAL_SPEC.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/TESTING.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/docs/VERSION.MD +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/pytest.ini +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/README.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/conftest.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/fixtures/__init__.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/fixtures/sample_code.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/fixtures/sample_docs.md +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_accuracy.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_architecture.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_async.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_behaviour.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_cache_stats.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_cli.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_flow.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_git.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_graph.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_graph_resolution_order.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_incremental.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_indexing.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_latency.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_mcp.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_mcp_hardening.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_observability.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_performance.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_query_cache_roundtrip.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_retrieval.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_retry.py +0 -0
- {contextual_engine-0.9.1 → contextual_engine-0.9.2}/tests/test_security.py +0 -0
- {contextual_engine-0.9.1 → 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,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextual-engine
|
|
3
|
-
Version: 0.9.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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":
|
|
252
|
-
"args": ["--
|
|
237
|
+
"command": contextual_bin,
|
|
238
|
+
"args": ["--workspace", str(workspace_root)],
|
|
253
239
|
}
|
|
254
240
|
|
|
255
241
|
|
|
256
|
-
def _build_claude_mcp_entry(
|
|
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":
|
|
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(
|
|
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":
|
|
295
|
-
"args": ["--
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
467
|
-
f'args = ["--
|
|
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,
|
|
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
|
-
|
|
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"{
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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"],
|
|
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(
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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.
|
|
@@ -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()
|