contextual-engine 0.9.4__tar.gz → 0.9.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/PKG-INFO +1 -1
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/index.py +10 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/initialisation.py +22 -0
- contextual_engine-0.9.5/contextual/cli/commands/workspace.py +100 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/indexing.py +4 -16
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/paths.py +1 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/workspace/retrieval_config.py +0 -2
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/git/incremental_git.py +65 -2
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/extractor.py +521 -10
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/integrator.py +4 -2
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/resolver.py +127 -8
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/traversal.py +9 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/__init__.py +2 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/_config.py +0 -4
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/pipeline.py +351 -107
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/__init__.py +1 -1
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/middleware/audit.py +1 -1
- contextual_engine-0.9.5/contextual/mcp/middleware/identity.py +185 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/middleware/workspace.py +13 -23
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/server.py +39 -23
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/session_manager.py +1 -107
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/shim.py +127 -54
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/tools/__init__.py +284 -1
- contextual_engine-0.9.5/contextual/mcp/tools/_gcf.py +279 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/tools/graph.py +205 -21
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/tools/nexus.py +65 -10
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/tools/semantic.py +164 -29
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/tools/system.py +150 -10
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/tools/temporal.py +22 -12
- contextual_engine-0.9.5/contextual/mcp/workspace_registry.py +410 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/__init__.py +20 -2
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/temporal/blame_pipeline.py +2 -2
- contextual_engine-0.9.5/docs/ADRs/ADR-006-remove-bm25-k1-b-dead-config.md +92 -0
- contextual_engine-0.9.5/docs/DRs/GEMINI-DR-analysis-extraction.md +319 -0
- contextual_engine-0.9.5/docs/DRs/GEMINI_DR_extraction-optimisation.md +158 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/pyproject.toml +1 -1
- contextual_engine-0.9.4/contextual/cli/commands/workspace.py +0 -53
- contextual_engine-0.9.4/contextual/mcp/middleware/identity.py +0 -65
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/.contextualignore +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/.cursor/rules/contextual.mdc +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/.github/workflows/ci.yml +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/.github/workflows/release.yml +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/.gitignore +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/.mcp.json +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/LICENSE +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/README.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/__main__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/auth.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/client.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/doctor.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/fetch.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/install.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/mcp.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/stats.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/core/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/core/context.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/core/decorators.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/core/editor.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/core/output.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/base.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/global_/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/global_/mcp_config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/global_/security_config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/global_/storage_config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/workspace/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/workspace/cache_config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/workspace/indexing_config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/workspace/observability_config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/config/workspace/workspace_config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/core/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/core/errors.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/core/models.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/git/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/git/blame.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/git/hooks.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/dedup.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/staleness.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/graph/store.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/incremental/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/incremental/daemon_embed.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/incremental/file_watcher.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/incremental/incremental_indexer.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/_ignore.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/chunker.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/docs_pipeline.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/index_writer.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/merkle_tree.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/processor.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/indexing/symbol_extractor.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/integrations/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/integrations/client_content.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/integrations/client_writer.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/__main__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/_platform.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/access.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/bootstrap.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/daemon.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/memory_monitor.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/middleware/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/middleware/auth.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/middleware/ratelimit.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/middleware/warmup.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/mcp/router.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/models/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/models/_inference.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/models/allowlist.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/models/base_embedder.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/models/reranker_model.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/models/unified_embedder.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/observability/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/observability/config.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/observability/exporters.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/observability/logging.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/observability/retention.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/observability/tracer.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/retrieval/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/retrieval/context_assembler.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/retrieval/mmr.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/retrieval/pipeline.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/retrieval/ranker.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/retrieval/tokenizer.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/security/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/security/paths.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/security/sanitize.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/security/workspace.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/cache.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/connection.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/fts.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/lancedb_provider.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me001_orgs.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me002_org_members.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me003_teams.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me004_team_members.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me005_org_workspaces.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me006_workspace_permissions.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me007_idp_sync_log.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me008_org_policies.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me009_api_keys.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me010_org_audit_log.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/enterprise/me011_workspace_traceability.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m001_global.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m002_embeddings.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m003_temporal.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m004_graph.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m005_cache.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m006_logs.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m007_terminal_sessions.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m008_queue.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m009_swarm.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m010_task_force.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m011_reminders.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m013_temporal_l2.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/m014_nexus_foundation.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/runner.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m001_global.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m002_embeddings.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m003_temporal.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m004_graph.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m005_cache.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m006_logs.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m007_terminal_sessions.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m008_queue.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m009_swarm.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m010_task_force.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m011_reminders.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m012_workspace_metadata.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m013_temporal_l2.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/solo/m014_nexus_foundation.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt001_orgs.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt002_org_members.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt003_teams.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt004_team_members.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt005_team_workspaces.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt006_workspace_permissions.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt007_team_audit_log.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/migrations/teams/mt008_workspace_traceability.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/schema.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/schema_base.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/schema_ent.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/storage/schema_team.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/temporal/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/temporal/adr.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/temporal/context.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/temporal/query.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/temporal/velocity.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/ADRs/ADR-001-lancedb-over-vec0.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/ADRs/ADR-002-retry-exponential-backoff.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/ADRs/ADR-003-blake3-deduplication.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/ADRs/ADR-004-async-first-architecture.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/ADRs/ADR-005-model-lineup.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/ARCHITECTURE.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/CHANGELOG.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DATABASE.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DEPLOYMENT.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DISTRIBUTIONS.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/CHATGPT-DR-mcp-cli-security-testing-distribution.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/CHATGPT-DR-web-ui-experience.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/CLAUDE-DR-ai-agents-teams.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/CLAUDE-DR-competitive-landscape-market-analysis.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/CLAUDE-DR-core-architecture.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/CLAUDE-DR-mvp-phase0-technical-plan.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/CLAUDE-DR-strategic-briefing.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GEMINI-DR-graph-implementation.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GEMINI-DR-hybrid-retrieval-local-optimization.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GEMINI-DR-infrastructure-security.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GEMINI-DR-lance-specs.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GEMINI-DR-nexus-graph-architecture.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GEMINI-DR-nexus-graph-engineering.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GEMINI-DR-vec0-vs-lancedb.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/GRAPH_LAYER_OPEN_QUESTIONS_RESOLVED.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/KIMI-DR-tech-stack-optimization.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-L2-temporal.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-client-integration.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-indexing-optimisation.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-lancedb-implementation.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-local-data-infrastructure.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-mcp-config.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-mcp-security-hardening.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-models-pipelines.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-python-async-ai-pipelines.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/DRs/PERPLEXITY-DR-semantic-indexing.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/FRONTEND.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/GITHUB.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/INTEGRATIONS.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/MCP_TOOLS.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/MEMORY.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/PROJECT_MANAGEMENT.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/PROJECT_PHASES.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/RESOURCES.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/ROADMAP.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/SECURITY.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/TEAMS.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/TECHNICAL_SPEC.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/TESTING.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/docs/VERSION.MD +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/pytest.ini +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/scripts/mcp_stress_test.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/README.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/conftest.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/fixtures/__init__.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/fixtures/sample_code.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/fixtures/sample_docs.md +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_accuracy.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_architecture.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_async.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_behaviour.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_cache_stats.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_cli.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_flow.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_git.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_graph.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_graph_resolution_order.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_incremental.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_indexing.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_latency.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_mcp.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_mcp_hardening.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_observability.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_performance.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_query_cache_roundtrip.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_retrieval.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_retry.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_security.py +0 -0
- {contextual_engine-0.9.4 → contextual_engine-0.9.5}/tests/test_storage.py +0 -0
|
@@ -47,6 +47,15 @@ async def index(
|
|
|
47
47
|
"-f",
|
|
48
48
|
help="Force full re-index even if already up-to-date",
|
|
49
49
|
),
|
|
50
|
+
bootstrap_cochange: bool = typer.Option(
|
|
51
|
+
False,
|
|
52
|
+
"--bootstrap-cochange",
|
|
53
|
+
help=(
|
|
54
|
+
"Walk up to 2000 git commits to pre-populate entity_commits for "
|
|
55
|
+
"co-change analysis. Only effective with --force. Commits touching "
|
|
56
|
+
">50 files are excluded as noise."
|
|
57
|
+
),
|
|
58
|
+
),
|
|
50
59
|
ctx: str | None = typer.Option(None, hidden=True),
|
|
51
60
|
) -> None:
|
|
52
61
|
"""Index codebase for semantic search."""
|
|
@@ -151,6 +160,7 @@ async def index(
|
|
|
151
160
|
context=cli_ctx.request_context,
|
|
152
161
|
force=force,
|
|
153
162
|
incremental=incremental,
|
|
163
|
+
bootstrap_cochange=bootstrap_cochange,
|
|
154
164
|
on_discovery=_on_discovery,
|
|
155
165
|
on_file_chunked=_on_file_chunked,
|
|
156
166
|
on_batch_embedded=_on_batch_embedded,
|
{contextual_engine-0.9.4 → contextual_engine-0.9.5}/contextual/cli/commands/initialisation.py
RENAMED
|
@@ -531,6 +531,28 @@ async def run_workspace_init(workspace_root: Path) -> None:
|
|
|
531
531
|
|
|
532
532
|
await _register_workspace_globally(global_conn, workspace_id, workspace_root, logger)
|
|
533
533
|
|
|
534
|
+
# Also register in workspaces.json for multi-workspace MCP support.
|
|
535
|
+
# Non-fatal: the DB registration above is authoritative for init; the
|
|
536
|
+
# registry entry is a convenience that `contextual workspace add` can
|
|
537
|
+
# replicate if this fails.
|
|
538
|
+
try:
|
|
539
|
+
from contextual.mcp.workspace_registry import WORKSPACE_REGISTRY
|
|
540
|
+
|
|
541
|
+
try:
|
|
542
|
+
WORKSPACE_REGISTRY.add_workspace(workspace_root.name, workspace_root)
|
|
543
|
+
logger.info(
|
|
544
|
+
"workspace_registry_json_added",
|
|
545
|
+
name=workspace_root.name,
|
|
546
|
+
path=str(workspace_root),
|
|
547
|
+
)
|
|
548
|
+
except ValueError:
|
|
549
|
+
logger.debug(
|
|
550
|
+
"workspace_registry_json_already_registered",
|
|
551
|
+
name=workspace_root.name,
|
|
552
|
+
)
|
|
553
|
+
except Exception as exc:
|
|
554
|
+
logger.warning("workspace_registry_json_add_failed", error=str(exc))
|
|
555
|
+
|
|
534
556
|
try:
|
|
535
557
|
if not (
|
|
536
558
|
config_path.exists()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Workspace management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from contextual.cli.core import register_command
|
|
11
|
+
from contextual.cli.core.output import print_error, print_workspace_list
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@register_command(group="workspace", name="list")
|
|
15
|
+
async def workspace_list() -> None:
|
|
16
|
+
"""List all registered workspaces."""
|
|
17
|
+
from contextual.mcp.workspace_registry import WORKSPACE_REGISTRY
|
|
18
|
+
|
|
19
|
+
ws_map = WORKSPACE_REGISTRY.load_registered_workspaces()
|
|
20
|
+
if not ws_map:
|
|
21
|
+
print_workspace_list([])
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
workspaces = []
|
|
25
|
+
for name, path_str in ws_map.items():
|
|
26
|
+
path = Path(path_str)
|
|
27
|
+
workspace_id = ""
|
|
28
|
+
last_indexed_at = "never"
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
state_file = path / ".contextual" / "state.json"
|
|
32
|
+
if state_file.exists():
|
|
33
|
+
state_data = json.loads(state_file.read_text(encoding="utf-8"))
|
|
34
|
+
last_indexed_at = state_data.get("last_indexed_at", "never")
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
from contextual.indexing._config import load_runtime_config
|
|
40
|
+
|
|
41
|
+
cfg = load_runtime_config(path)
|
|
42
|
+
workspace_id = str(cfg.workspace_id)
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
workspaces.append({
|
|
47
|
+
"path": f"{name} {path_str}",
|
|
48
|
+
"workspace_id": workspace_id,
|
|
49
|
+
"last_indexed_at": last_indexed_at,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
print_workspace_list(workspaces)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@register_command(group="workspace", name="add")
|
|
56
|
+
async def workspace_add(
|
|
57
|
+
path: str = typer.Argument(..., help="Absolute path to the workspace root"),
|
|
58
|
+
name: str = typer.Option(
|
|
59
|
+
None,
|
|
60
|
+
"--name",
|
|
61
|
+
"-n",
|
|
62
|
+
help="Human-readable workspace name (defaults to directory basename)",
|
|
63
|
+
),
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Register a workspace in the multi-workspace registry."""
|
|
66
|
+
from contextual.mcp.workspace_registry import WORKSPACE_REGISTRY
|
|
67
|
+
|
|
68
|
+
resolved = Path(path).expanduser().resolve()
|
|
69
|
+
ws_name = name or resolved.name
|
|
70
|
+
if not ws_name:
|
|
71
|
+
print_error("Could not derive a workspace name.", hint="Use --name <name>")
|
|
72
|
+
raise typer.Exit(1)
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
WORKSPACE_REGISTRY.add_workspace(ws_name, resolved)
|
|
76
|
+
typer.echo(f"Registered workspace '{ws_name}' at {resolved}")
|
|
77
|
+
except ValueError as exc:
|
|
78
|
+
print_error(str(exc))
|
|
79
|
+
raise typer.Exit(1) from exc
|
|
80
|
+
except Exception as exc:
|
|
81
|
+
print_error(f"Failed to register workspace: {exc}")
|
|
82
|
+
raise typer.Exit(1) from exc
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@register_command(group="workspace", name="remove")
|
|
86
|
+
async def workspace_remove(
|
|
87
|
+
name: str = typer.Argument(..., help="Workspace name to deregister"),
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Remove a workspace from the multi-workspace registry."""
|
|
90
|
+
from contextual.mcp.workspace_registry import WORKSPACE_REGISTRY
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
WORKSPACE_REGISTRY.remove_workspace(name)
|
|
94
|
+
typer.echo(f"Removed workspace '{name}'")
|
|
95
|
+
except KeyError as exc:
|
|
96
|
+
print_error(str(exc))
|
|
97
|
+
raise typer.Exit(1) from exc
|
|
98
|
+
except Exception as exc:
|
|
99
|
+
print_error(f"Failed to remove workspace: {exc}")
|
|
100
|
+
raise typer.Exit(1) from exc
|
|
@@ -113,7 +113,7 @@ class SearchConfig(BaseModel):
|
|
|
113
113
|
le=200,
|
|
114
114
|
description="Number of candidates retrieved before reranking.",
|
|
115
115
|
)
|
|
116
|
-
enable_reranker: bool = Field(default=
|
|
116
|
+
enable_reranker: bool = Field(default=False) # 124x latency overhead with no accuracy gain on MPS
|
|
117
117
|
rerank_top_k: int = Field(
|
|
118
118
|
default=10,
|
|
119
119
|
ge=5,
|
|
@@ -149,12 +149,10 @@ class RetrievalConfig:
|
|
|
149
149
|
bm25_candidates: int = 100
|
|
150
150
|
dense_candidates: int = 100
|
|
151
151
|
rrf_candidates: int = 50
|
|
152
|
-
bm25_k1: float = 1.2
|
|
153
|
-
bm25_b: float = 0.3
|
|
154
152
|
rrf_k: int = 60
|
|
155
153
|
rrf_dense_weight: float = 0.6
|
|
156
154
|
rrf_bm25_weight: float = 0.4
|
|
157
|
-
reranker_enabled: bool =
|
|
155
|
+
reranker_enabled: bool = False # 124x latency overhead with no accuracy gain on MPS
|
|
158
156
|
reranker_top_n: int = 15
|
|
159
157
|
reranker_max_chunk_chars: int = 2000
|
|
160
158
|
mmr_enabled: bool = True
|
|
@@ -195,8 +193,6 @@ class RetrievalConfig:
|
|
|
195
193
|
retrieval_data.get("dense_candidates", defaults.dense_candidates)
|
|
196
194
|
),
|
|
197
195
|
rrf_candidates=int(retrieval_data.get("rrf_candidates", defaults.rrf_candidates)),
|
|
198
|
-
bm25_k1=float(retrieval_data.get("bm25_k1", defaults.bm25_k1)),
|
|
199
|
-
bm25_b=float(retrieval_data.get("bm25_b", defaults.bm25_b)),
|
|
200
196
|
rrf_k=int(retrieval_data.get("rrf_k", defaults.rrf_k)),
|
|
201
197
|
rrf_dense_weight=float(
|
|
202
198
|
retrieval_data.get("rrf_dense_weight", defaults.rrf_dense_weight)
|
|
@@ -316,12 +312,10 @@ class RuntimeIndexingConfig:
|
|
|
316
312
|
|
|
317
313
|
@dataclass(slots=True)
|
|
318
314
|
class RuntimeRetrievalConfig:
|
|
319
|
-
bm25_k1: float = 1.2
|
|
320
|
-
bm25_b: float = 0.3
|
|
321
315
|
rrf_k: int = 60
|
|
322
316
|
rrf_dense_weight: float = 0.6
|
|
323
317
|
rrf_bm25_weight: float = 0.4
|
|
324
|
-
reranker_enabled: bool =
|
|
318
|
+
reranker_enabled: bool = False # 124x latency overhead with no accuracy gain on MPS
|
|
325
319
|
reranker_top_n: int = 15
|
|
326
320
|
mmr_lambda: float = 0.7
|
|
327
321
|
mmr_max_chunks_per_file: int = 2
|
|
@@ -393,12 +387,10 @@ class ConfigLoader:
|
|
|
393
387
|
"workers": 8,
|
|
394
388
|
},
|
|
395
389
|
"retrieval": {
|
|
396
|
-
"bm25_k1": 1.2,
|
|
397
|
-
"bm25_b": 0.3,
|
|
398
390
|
"rrf_k": 60,
|
|
399
391
|
"rrf_dense_weight": 0.6,
|
|
400
392
|
"rrf_bm25_weight": 0.4,
|
|
401
|
-
"reranker_enabled":
|
|
393
|
+
"reranker_enabled": False, # 124x latency overhead with no accuracy gain on MPS
|
|
402
394
|
"reranker_top_n": 15,
|
|
403
395
|
"mmr_lambda": 0.7,
|
|
404
396
|
"mmr_max_chunks_per_file": 2,
|
|
@@ -437,10 +429,6 @@ class ConfigLoader:
|
|
|
437
429
|
env_overrides["indexing"]["chunk_overlap"] = int(value)
|
|
438
430
|
if value := os.getenv("CONTEXTUAL_MAX_FILE_SIZE_KB"):
|
|
439
431
|
env_overrides["indexing"]["max_file_size_kb"] = int(value)
|
|
440
|
-
if value := os.getenv("CONTEXTUAL_BM25_K1"):
|
|
441
|
-
env_overrides["retrieval"]["bm25_k1"] = float(value)
|
|
442
|
-
if value := os.getenv("CONTEXTUAL_BM25_B"):
|
|
443
|
-
env_overrides["retrieval"]["bm25_b"] = float(value)
|
|
444
432
|
if value := os.getenv("CONTEXTUAL_RRF_K"):
|
|
445
433
|
env_overrides["retrieval"]["rrf_k"] = int(value)
|
|
446
434
|
if value := os.getenv("CONTEXTUAL_RRF_DENSE_WEIGHT"):
|
|
@@ -43,6 +43,7 @@ DAEMON_LOCK = CONTEXTUAL_HOME / "daemon.lock"
|
|
|
43
43
|
DAEMON_LOG = CONTEXTUAL_HOME / "daemon.log"
|
|
44
44
|
VERSION_FILE = CONTEXTUAL_HOME / ".version"
|
|
45
45
|
MCP_CONFIG_FILE = CONTEXTUAL_HOME / "mcp.json"
|
|
46
|
+
WORKSPACES_JSON_PATH = CONTEXTUAL_HOME / "workspaces.json"
|
|
46
47
|
LOG_DIR = Path.home() / ".local" / "state" / "contextual" / "logs"
|
|
47
48
|
|
|
48
49
|
# Canonical per-model weight directories. `contextual fetch` downloads into
|
|
@@ -202,6 +202,7 @@ class GitIntegration:
|
|
|
202
202
|
return []
|
|
203
203
|
|
|
204
204
|
entries: list[BlameEntry] = []
|
|
205
|
+
current_line = 1
|
|
205
206
|
for (commit, tree_entry), content in blamed:
|
|
206
207
|
try:
|
|
207
208
|
text = content.decode("utf-8", errors="replace")
|
|
@@ -232,11 +233,12 @@ class GitIntegration:
|
|
|
232
233
|
timestamp=datetime.fromtimestamp(commit.commit_time, tz=UTC),
|
|
233
234
|
commit_sha=commit.id.decode("ascii"),
|
|
234
235
|
commit_message=commit_message,
|
|
235
|
-
line_start=
|
|
236
|
-
line_end=line_count,
|
|
236
|
+
line_start=current_line,
|
|
237
|
+
line_end=current_line + line_count - 1,
|
|
237
238
|
path=tree_entry.path.decode("utf-8", errors="replace"),
|
|
238
239
|
)
|
|
239
240
|
)
|
|
241
|
+
current_line += line_count
|
|
240
242
|
|
|
241
243
|
return entries
|
|
242
244
|
|
|
@@ -281,6 +283,67 @@ def get_git_integration(repo_path: Path | str) -> GitIntegration:
|
|
|
281
283
|
return integration
|
|
282
284
|
|
|
283
285
|
|
|
286
|
+
async def walk_commit_history(
|
|
287
|
+
repo_root: Path,
|
|
288
|
+
max_commits: int = 2000,
|
|
289
|
+
max_files_per_commit: int = 50,
|
|
290
|
+
) -> list[dict]:
|
|
291
|
+
"""Walk git commit history, returning per-commit file change sets.
|
|
292
|
+
|
|
293
|
+
Commits touching more than ``max_files_per_commit`` files are excluded —
|
|
294
|
+
bulk operations (vendor drops, mass-renames) add noise to co-change coupling
|
|
295
|
+
and inflate pair counts across the entire repo.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
repo_root: Absolute path to the repository root.
|
|
299
|
+
max_commits: Maximum commits to inspect (most recent first).
|
|
300
|
+
max_files_per_commit: Commits with more changed files are skipped.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
List of ``{commit_hash, timestamp, author, files_changed}`` dicts,
|
|
304
|
+
ordered most-recent first.
|
|
305
|
+
"""
|
|
306
|
+
|
|
307
|
+
def _walk_sync() -> list[dict]:
|
|
308
|
+
try:
|
|
309
|
+
import pygit2 # type: ignore[import-untyped]
|
|
310
|
+
except ImportError:
|
|
311
|
+
return []
|
|
312
|
+
try:
|
|
313
|
+
repo = pygit2.Repository(str(repo_root))
|
|
314
|
+
head_target = repo.head.target
|
|
315
|
+
except Exception:
|
|
316
|
+
return []
|
|
317
|
+
|
|
318
|
+
results: list[dict] = []
|
|
319
|
+
count = 0
|
|
320
|
+
for commit in repo.walk(head_target, pygit2.GIT_SORT_TIME):
|
|
321
|
+
if count >= max_commits:
|
|
322
|
+
break
|
|
323
|
+
count += 1
|
|
324
|
+
try:
|
|
325
|
+
if commit.parents:
|
|
326
|
+
diff = repo.diff(commit.parents[0], commit)
|
|
327
|
+
else:
|
|
328
|
+
diff = commit.tree.diff_to_tree()
|
|
329
|
+
files = {d.new_file.path for d in diff.deltas if d.new_file.path}
|
|
330
|
+
if len(files) > max_files_per_commit:
|
|
331
|
+
continue
|
|
332
|
+
results.append(
|
|
333
|
+
{
|
|
334
|
+
"commit_hash": str(commit.id),
|
|
335
|
+
"timestamp": datetime.fromtimestamp(commit.commit_time, tz=UTC),
|
|
336
|
+
"author": commit.author.name,
|
|
337
|
+
"files_changed": sorted(files),
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
except Exception:
|
|
341
|
+
continue
|
|
342
|
+
return results
|
|
343
|
+
|
|
344
|
+
return await asyncio.to_thread(_walk_sync)
|
|
345
|
+
|
|
346
|
+
|
|
284
347
|
import atexit
|
|
285
348
|
|
|
286
349
|
|