attocode-code-intel 0.1.0__py3-none-any.whl
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.
- attocode_code_intel-0.1.0.dist-info/METADATA +221 -0
- attocode_code_intel-0.1.0.dist-info/RECORD +345 -0
- attocode_code_intel-0.1.0.dist-info/WHEEL +4 -0
- attocode_code_intel-0.1.0.dist-info/entry_points.txt +2 -0
- attocode_code_intel-0.1.0.dist-info/licenses/LICENSE +21 -0
- attocode_intel/GUIDELINES.md +432 -0
- attocode_intel/__init__.py +52 -0
- attocode_intel/_internal/__init__.py +1 -0
- attocode_intel/_internal/errors/__init__.py +126 -0
- attocode_intel/_internal/integrations/__init__.py +1 -0
- attocode_intel/_internal/integrations/context/__init__.py +1 -0
- attocode_intel/_internal/integrations/context/architecture_drift.py +354 -0
- attocode_intel/_internal/integrations/context/ast_chunker.py +202 -0
- attocode_intel/_internal/integrations/context/ast_service.py +1483 -0
- attocode_intel/_internal/integrations/context/blob_oid.py +423 -0
- attocode_intel/_internal/integrations/context/cache_manifest.py +221 -0
- attocode_intel/_internal/integrations/context/cas.py +466 -0
- attocode_intel/_internal/integrations/context/code_analyzer.py +528 -0
- attocode_intel/_internal/integrations/context/codebase_ast.py +1461 -0
- attocode_intel/_internal/integrations/context/codebase_context.py +1736 -0
- attocode_intel/_internal/integrations/context/cross_mode.py +263 -0
- attocode_intel/_internal/integrations/context/cross_references.py +616 -0
- attocode_intel/_internal/integrations/context/embedding_rotation.py +597 -0
- attocode_intel/_internal/integrations/context/embeddings.py +282 -0
- attocode_intel/_internal/integrations/context/frecency.py +427 -0
- attocode_intel/_internal/integrations/context/fuzzy.py +320 -0
- attocode_intel/_internal/integrations/context/hierarchical_explorer.py +331 -0
- attocode_intel/_internal/integrations/context/hydration.py +90 -0
- attocode_intel/_internal/integrations/context/index_repair.py +87 -0
- attocode_intel/_internal/integrations/context/index_store.py +586 -0
- attocode_intel/_internal/integrations/context/memory_store.py +602 -0
- attocode_intel/_internal/integrations/context/module_resolution.py +52 -0
- attocode_intel/_internal/integrations/context/query_constraints.py +361 -0
- attocode_intel/_internal/integrations/context/query_history.py +417 -0
- attocode_intel/_internal/integrations/context/reranker.py +351 -0
- attocode_intel/_internal/integrations/context/semantic_search.py +2931 -0
- attocode_intel/_internal/integrations/context/syntax_evidence.py +94 -0
- attocode_intel/_internal/integrations/context/systemone_ranker.py +245 -0
- attocode_intel/_internal/integrations/context/temporal_coupling.py +411 -0
- attocode_intel/_internal/integrations/context/trigram_index.py +820 -0
- attocode_intel/_internal/integrations/context/trigram_regex.py +171 -0
- attocode_intel/_internal/integrations/context/ts_parser.py +2199 -0
- attocode_intel/_internal/integrations/context/vector_store.py +795 -0
- attocode_intel/_internal/integrations/feature_flags/__init__.py +357 -0
- attocode_intel/_internal/integrations/lsp/__init__.py +1 -0
- attocode_intel/_internal/integrations/lsp/client.py +1140 -0
- attocode_intel/_internal/integrations/mcp/__init__.py +1 -0
- attocode_intel/_internal/integrations/mcp/client.py +253 -0
- attocode_intel/_internal/integrations/security/__init__.py +1 -0
- attocode_intel/_internal/integrations/security/custom_rules.py +176 -0
- attocode_intel/_internal/integrations/security/dataflow.py +522 -0
- attocode_intel/_internal/integrations/security/dependency_audit.py +303 -0
- attocode_intel/_internal/integrations/security/matcher.py +44 -0
- attocode_intel/_internal/integrations/security/patterns.py +724 -0
- attocode_intel/_internal/integrations/security/scanner.py +351 -0
- attocode_intel/_internal/integrations/utilities/__init__.py +1 -0
- attocode_intel/_internal/integrations/utilities/ignore.py +64 -0
- attocode_intel/_internal/integrations/utilities/token_estimate.py +51 -0
- attocode_intel/_internal/tricks/__init__.py +1 -0
- attocode_intel/_internal/tricks/recursive_context.py +357 -0
- attocode_intel/_internal/types/__init__.py +1 -0
- attocode_intel/_internal/types/events.py +289 -0
- attocode_intel/_internal/types/severity.py +15 -0
- attocode_intel/_shared.py +341 -0
- attocode_intel/analysis_coverage.py +56 -0
- attocode_intel/api/__init__.py +1 -0
- attocode_intel/api/access.py +32 -0
- attocode_intel/api/app.py +247 -0
- attocode_intel/api/auth/__init__.py +128 -0
- attocode_intel/api/auth/api_keys.py +61 -0
- attocode_intel/api/auth/context.py +34 -0
- attocode_intel/api/auth/jwt.py +66 -0
- attocode_intel/api/auth/oauth.py +146 -0
- attocode_intel/api/auth/passwords.py +15 -0
- attocode_intel/api/deps.py +351 -0
- attocode_intel/api/knowledge_bridge.py +13 -0
- attocode_intel/api/mcp_transport.py +51 -0
- attocode_intel/api/middleware.py +411 -0
- attocode_intel/api/models.py +698 -0
- attocode_intel/api/providers/__init__.py +1 -0
- attocode_intel/api/providers/db_provider.py +1106 -0
- attocode_intel/api/providers/local_provider.py +476 -0
- attocode_intel/api/providers/remote_provider.py +740 -0
- attocode_intel/api/repository_source.py +21 -0
- attocode_intel/api/routes/__init__.py +1 -0
- attocode_intel/api/routes/_pin_helper.py +158 -0
- attocode_intel/api/routes/activity.py +148 -0
- attocode_intel/api/routes/adr.py +102 -0
- attocode_intel/api/routes/analysis.py +515 -0
- attocode_intel/api/routes/api_keys.py +170 -0
- attocode_intel/api/routes/auth.py +428 -0
- attocode_intel/api/routes/branches.py +262 -0
- attocode_intel/api/routes/cross_repo_search.py +198 -0
- attocode_intel/api/routes/embeddings.py +386 -0
- attocode_intel/api/routes/files.py +132 -0
- attocode_intel/api/routes/files_v2.py +388 -0
- attocode_intel/api/routes/gc.py +322 -0
- attocode_intel/api/routes/git_v2.py +433 -0
- attocode_intel/api/routes/graph.py +174 -0
- attocode_intel/api/routes/graph_viz.py +160 -0
- attocode_intel/api/routes/health.py +62 -0
- attocode_intel/api/routes/history.py +175 -0
- attocode_intel/api/routes/jobs.py +84 -0
- attocode_intel/api/routes/learning.py +205 -0
- attocode_intel/api/routes/lsp.py +272 -0
- attocode_intel/api/routes/notify.py +623 -0
- attocode_intel/api/routes/operations.py +27 -0
- attocode_intel/api/routes/orgs.py +924 -0
- attocode_intel/api/routes/preferences.py +100 -0
- attocode_intel/api/routes/presence.py +42 -0
- attocode_intel/api/routes/projects.py +116 -0
- attocode_intel/api/routes/repos.py +47 -0
- attocode_intel/api/routes/search.py +180 -0
- attocode_intel/api/routes/snapshots.py +614 -0
- attocode_intel/api/routes/webhooks.py +313 -0
- attocode_intel/api/routes/websocket.py +116 -0
- attocode_intel/api/static/graph.html +239 -0
- attocode_intel/api/utils.py +96 -0
- attocode_intel/artifacts/__init__.py +45 -0
- attocode_intel/artifacts/hashing.py +230 -0
- attocode_intel/artifacts/provenance.py +190 -0
- attocode_intel/artifacts/retrieval_pin.py +159 -0
- attocode_intel/audit.py +59 -0
- attocode_intel/bug_finder.py +206 -0
- attocode_intel/bundle.py +80 -0
- attocode_intel/catalog.py +242 -0
- attocode_intel/change_review.py +75 -0
- attocode_intel/cli.py +3062 -0
- attocode_intel/community.py +63 -0
- attocode_intel/confidence/__init__.py +144 -0
- attocode_intel/confidence/jev.py +234 -0
- attocode_intel/confidence/llm.py +330 -0
- attocode_intel/confidence/redact.py +44 -0
- attocode_intel/confidence/settings.py +74 -0
- attocode_intel/config.py +256 -0
- attocode_intel/crypto.py +31 -0
- attocode_intel/db/__init__.py +7 -0
- attocode_intel/db/base.py +33 -0
- attocode_intel/db/engine.py +61 -0
- attocode_intel/db/models.py +529 -0
- attocode_intel/entrypoint.py +185 -0
- attocode_intel/focused_evidence.py +130 -0
- attocode_intel/freshness.py +154 -0
- attocode_intel/gateway.py +734 -0
- attocode_intel/git/__init__.py +7 -0
- attocode_intel/git/credentials.py +75 -0
- attocode_intel/git/manager.py +493 -0
- attocode_intel/git/models.py +96 -0
- attocode_intel/git/storage.py +98 -0
- attocode_intel/graph_query_parser.py +761 -0
- attocode_intel/helpers.py +712 -0
- attocode_intel/indexing/__init__.py +29 -0
- attocode_intel/indexing/debouncer.py +136 -0
- attocode_intel/indexing/delta_indexer.py +151 -0
- attocode_intel/indexing/full_indexer.py +323 -0
- attocode_intel/indexing/incremental.py +240 -0
- attocode_intel/indexing/parser.py +403 -0
- attocode_intel/indexing/summarizer.py +264 -0
- attocode_intel/installer.py +1896 -0
- attocode_intel/knowledge.py +269 -0
- attocode_intel/local_knowledge.py +77 -0
- attocode_intel/migrations/alembic.ini +36 -0
- attocode_intel/migrations/env.py +71 -0
- attocode_intel/migrations/script.py.mako +27 -0
- attocode_intel/migrations/versions/001_initial_schema.py +219 -0
- attocode_intel/migrations/versions/002_branch_version_and_embeddings.py +61 -0
- attocode_intel/migrations/versions/003_embedding_uniqueness_and_cascade.py +51 -0
- attocode_intel/migrations/versions/004_audit_events.py +38 -0
- attocode_intel/migrations/versions/005_user_preferences.py +30 -0
- attocode_intel/migrations/versions/006_google_oauth.py +27 -0
- attocode_intel/migrations/versions/007_pgvector_embeddings.py +28 -0
- attocode_intel/migrations/versions/008_symbol_references.py +37 -0
- attocode_intel/migrations/versions/009_webhook_secret_encryption.py +25 -0
- attocode_intel/migrations/versions/010_add_commits_table.py +40 -0
- attocode_intel/migrations/versions/011_commit_changed_files.py +25 -0
- attocode_intel/migrations/versions/012_branch_merged_at.py +25 -0
- attocode_intel/migrations/versions/013_revoked_tokens.py +32 -0
- attocode_intel/migrations/versions/014_blame_hunks.py +40 -0
- attocode_intel/migrations/versions/015_commit_file_stats.py +40 -0
- attocode_intel/migrations/versions/016_embedding_provenance_columns.py +179 -0
- attocode_intel/migrations/versions/017_provenance_table.py +102 -0
- attocode_intel/migrations/versions/018_repo_snapshots.py +178 -0
- attocode_intel/migrations/versions/019_symref_caller.py +42 -0
- attocode_intel/migrations/versions/020_shared_knowledge.py +29 -0
- attocode_intel/onboarding.py +258 -0
- attocode_intel/output.py +178 -0
- attocode_intel/precision.py +133 -0
- attocode_intel/presence.py +160 -0
- attocode_intel/probe.py +110 -0
- attocode_intel/project_dir.py +97 -0
- attocode_intel/pubsub.py +113 -0
- attocode_intel/query_ranking.py +242 -0
- attocode_intel/readiness.py +972 -0
- attocode_intel/redis.py +51 -0
- attocode_intel/reference_pages.py +60 -0
- attocode_intel/remote.py +131 -0
- attocode_intel/repo_ranker.py +246 -0
- attocode_intel/request_context.py +82 -0
- attocode_intel/rules/__init__.py +28 -0
- attocode_intel/rules/ci.py +320 -0
- attocode_intel/rules/combinators.py +293 -0
- attocode_intel/rules/enricher.py +127 -0
- attocode_intel/rules/evolution.py +452 -0
- attocode_intel/rules/executor.py +531 -0
- attocode_intel/rules/filters/__init__.py +5 -0
- attocode_intel/rules/filters/pipeline.py +151 -0
- attocode_intel/rules/formatter.py +198 -0
- attocode_intel/rules/hygiene.py +242 -0
- attocode_intel/rules/importers/__init__.py +1 -0
- attocode_intel/rules/importers/semgrep.py +382 -0
- attocode_intel/rules/loader.py +292 -0
- attocode_intel/rules/marketplace.py +375 -0
- attocode_intel/rules/metavar.py +181 -0
- attocode_intel/rules/model.py +317 -0
- attocode_intel/rules/packs/__init__.py +1 -0
- attocode_intel/rules/packs/community/_license_templates/Apache-2.0.txt +176 -0
- attocode_intel/rules/packs/community/_license_templates/MIT.txt +21 -0
- attocode_intel/rules/packs/community/bandit-python/LICENSE +176 -0
- attocode_intel/rules/packs/community/bandit-python/NOTICE +13 -0
- attocode_intel/rules/packs/community/bandit-python/PORTING.md +23 -0
- attocode_intel/rules/packs/community/bandit-python/manifest.yaml +14 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B105-hardcoded-password.yaml +23 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B303-weak-md5.yaml +21 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B304-weak-des.yaml +20 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B307-use-of-eval.yaml +22 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B324-weak-sha1.yaml +21 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B501-no-cert-validation.yaml +23 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B602-shell-true.yaml +22 -0
- attocode_intel/rules/packs/community/bandit-python/rules/B608-sqli.yaml +22 -0
- attocode_intel/rules/packs/community/eslint-typescript/LICENSE +21 -0
- attocode_intel/rules/packs/community/eslint-typescript/NOTICE +13 -0
- attocode_intel/rules/packs/community/eslint-typescript/PORTING.md +23 -0
- attocode_intel/rules/packs/community/eslint-typescript/manifest.yaml +14 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/eqeqeq.yaml +20 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-eval.yaml +22 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-implied-eval.yaml +20 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-new-func.yaml +21 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-throw-literal.yaml +19 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-var.yaml +20 -0
- attocode_intel/rules/packs/community/eslint-typescript/rules/no-with.yaml +18 -0
- attocode_intel/rules/packs/community/gosec-go/LICENSE +176 -0
- attocode_intel/rules/packs/community/gosec-go/NOTICE +13 -0
- attocode_intel/rules/packs/community/gosec-go/PORTING.md +23 -0
- attocode_intel/rules/packs/community/gosec-go/manifest.yaml +14 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G101-hardcoded-credentials.yaml +22 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G102-bind-all-interfaces.yaml +21 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G103-unsafe-block.yaml +21 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G201-sql-format.yaml +21 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G304-file-inclusion.yaml +23 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G401-weak-cipher.yaml +20 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G402-tls-min-version.yaml +20 -0
- attocode_intel/rules/packs/community/gosec-go/rules/G501-md5-import.yaml +21 -0
- attocode_intel/rules/packs/examples/cpp/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/cpp/rules/security.yaml +90 -0
- attocode_intel/rules/packs/examples/cpp/rules/structural.yaml +42 -0
- attocode_intel/rules/packs/examples/csharp/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/csharp/rules/security.yaml +69 -0
- attocode_intel/rules/packs/examples/csharp/rules/structural.yaml +42 -0
- attocode_intel/rules/packs/examples/go/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/go/rules/performance.yaml +136 -0
- attocode_intel/rules/packs/examples/go/rules/security.yaml +48 -0
- attocode_intel/rules/packs/examples/go/rules/structural.yaml +58 -0
- attocode_intel/rules/packs/examples/go/taint/sanitizers.yaml +24 -0
- attocode_intel/rules/packs/examples/go/taint/sinks.yaml +51 -0
- attocode_intel/rules/packs/examples/go/taint/sources.yaml +26 -0
- attocode_intel/rules/packs/examples/java/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/java/rules/correctness.yaml +55 -0
- attocode_intel/rules/packs/examples/java/rules/structural.yaml +48 -0
- attocode_intel/rules/packs/examples/java/taint/sinks.yaml +37 -0
- attocode_intel/rules/packs/examples/java/taint/sources.yaml +21 -0
- attocode_intel/rules/packs/examples/kotlin/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/kotlin/rules/correctness.yaml +50 -0
- attocode_intel/rules/packs/examples/kotlin/rules/structural.yaml +38 -0
- attocode_intel/rules/packs/examples/php/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/php/rules/security.yaml +66 -0
- attocode_intel/rules/packs/examples/php/rules/structural.yaml +41 -0
- attocode_intel/rules/packs/examples/python/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/python/rules/performance.yaml +90 -0
- attocode_intel/rules/packs/examples/python/rules/structural.yaml +55 -0
- attocode_intel/rules/packs/examples/ruby/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/ruby/rules/security.yaml +73 -0
- attocode_intel/rules/packs/examples/ruby/rules/structural.yaml +41 -0
- attocode_intel/rules/packs/examples/rust/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/rust/rules/correctness.yaml +51 -0
- attocode_intel/rules/packs/examples/rust/rules/structural.yaml +49 -0
- attocode_intel/rules/packs/examples/typescript/manifest.yaml +4 -0
- attocode_intel/rules/packs/examples/typescript/rules/performance.yaml +70 -0
- attocode_intel/rules/packs/examples/typescript/rules/structural.yaml +48 -0
- attocode_intel/rules/packs/pack_loader.py +244 -0
- attocode_intel/rules/plugins/__init__.py +1 -0
- attocode_intel/rules/plugins/plugin_loader.py +132 -0
- attocode_intel/rules/profiling.py +332 -0
- attocode_intel/rules/registry.py +213 -0
- attocode_intel/rules/sarif.py +187 -0
- attocode_intel/rules/synthesis.py +420 -0
- attocode_intel/rules/testing.py +326 -0
- attocode_intel/server.py +734 -0
- attocode_intel/service.py +3416 -0
- attocode_intel/sharing.py +57 -0
- attocode_intel/storage/__init__.py +6 -0
- attocode_intel/storage/branch_overlay.py +352 -0
- attocode_intel/storage/content_store.py +220 -0
- attocode_intel/storage/dependency_store.py +143 -0
- attocode_intel/storage/diff_engine.py +187 -0
- attocode_intel/storage/embedding_store.py +594 -0
- attocode_intel/storage/provenance_store.py +137 -0
- attocode_intel/storage/security_scanner_db.py +164 -0
- attocode_intel/storage/symbol_store.py +160 -0
- attocode_intel/structural_evidence.py +144 -0
- attocode_intel/symbol_inspection.py +141 -0
- attocode_intel/symbol_links.py +167 -0
- attocode_intel/telemetry.py +42 -0
- attocode_intel/test_ranking.py +73 -0
- attocode_intel/testing/__init__.py +43 -0
- attocode_intel/testing/fixtures.py +122 -0
- attocode_intel/testing/helpers.py +75 -0
- attocode_intel/testing/mocks.py +99 -0
- attocode_intel/tools/__init__.py +2 -0
- attocode_intel/tools/adr_tools.py +628 -0
- attocode_intel/tools/analysis_tools.py +382 -0
- attocode_intel/tools/composite_tools.py +695 -0
- attocode_intel/tools/cross_mode_tools.py +173 -0
- attocode_intel/tools/dead_code_tools.py +582 -0
- attocode_intel/tools/distill_tools.py +353 -0
- attocode_intel/tools/frecency_tools.py +220 -0
- attocode_intel/tools/fuzzy_tools.py +255 -0
- attocode_intel/tools/history_tools.py +486 -0
- attocode_intel/tools/learning_tools.py +222 -0
- attocode_intel/tools/lsp_tools.py +218 -0
- attocode_intel/tools/maintenance_tools.py +1662 -0
- attocode_intel/tools/navigation_tools.py +220 -0
- attocode_intel/tools/overlay_tools.py +540 -0
- attocode_intel/tools/pin_store.py +432 -0
- attocode_intel/tools/pin_tools.py +266 -0
- attocode_intel/tools/query_constraints_tools.py +262 -0
- attocode_intel/tools/query_history_tools.py +158 -0
- attocode_intel/tools/readiness_tools.py +44 -0
- attocode_intel/tools/rule_tools.py +992 -0
- attocode_intel/tools/search_tools.py +770 -0
- attocode_intel/tools/snapshot_tools.py +624 -0
- attocode_intel/workers/__init__.py +1 -0
- attocode_intel/workers/job_utils.py +111 -0
- attocode_intel/workers/jobs.py +573 -0
- attocode_intel/workers/run.py +30 -0
- attocode_intel/workers/settings.py +61 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""Query constraints MCP tools.
|
|
2
|
+
|
|
3
|
+
Supports fff-style query constraints for filtering search results:
|
|
4
|
+
- git:modified, git:staged, git:deleted, git:renamed, git:untracked, git:ignored
|
|
5
|
+
- !pattern - exclude files matching pattern
|
|
6
|
+
- path/ - filter by directory
|
|
7
|
+
- ./**/*.py - glob patterns
|
|
8
|
+
- *.py - extension filters
|
|
9
|
+
|
|
10
|
+
Example queries:
|
|
11
|
+
"git:modified *.py" - Modified Python files
|
|
12
|
+
"!test/ src/**/*.rs" - Rust files not in test directories
|
|
13
|
+
"git:staged !vendor/" - Staged files excluding vendor
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import subprocess
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
from attocode_intel._shared import _get_project_dir, mcp
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from attocode_intel._internal.integrations.context.query_constraints import QueryConstraintProcessor
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_constraint_processor() -> QueryConstraintProcessor:
|
|
28
|
+
"""Get the constraint processor."""
|
|
29
|
+
from attocode_intel._internal.integrations.context.query_constraints import (
|
|
30
|
+
QueryConstraintProcessor,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
project_dir = _get_project_dir()
|
|
34
|
+
return QueryConstraintProcessor(project_dir=project_dir)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _run_git_status() -> dict[str, str]:
|
|
38
|
+
"""Run git status --porcelain to get all file statuses.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Dict mapping file path to status code.
|
|
42
|
+
"""
|
|
43
|
+
project_dir = _get_project_dir()
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
result = subprocess.run(
|
|
47
|
+
["git", "status", "--porcelain"],
|
|
48
|
+
cwd=project_dir,
|
|
49
|
+
capture_output=True,
|
|
50
|
+
text=True,
|
|
51
|
+
timeout=30,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
statuses: dict[str, str] = {}
|
|
55
|
+
for line in result.stdout.splitlines():
|
|
56
|
+
if len(line) < 3:
|
|
57
|
+
continue
|
|
58
|
+
x_status = line[0] # index (staging area)
|
|
59
|
+
y_status = line[1] # worktree
|
|
60
|
+
file_path = line[3:].strip()
|
|
61
|
+
# Prefer index status if meaningful, otherwise use worktree
|
|
62
|
+
if x_status not in (" ", "?"):
|
|
63
|
+
statuses[file_path] = x_status
|
|
64
|
+
elif y_status != " ":
|
|
65
|
+
statuses[file_path] = y_status
|
|
66
|
+
else:
|
|
67
|
+
statuses[file_path] = x_status # handles '??' etc.
|
|
68
|
+
|
|
69
|
+
return statuses
|
|
70
|
+
|
|
71
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
72
|
+
return {}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@mcp.tool()
|
|
76
|
+
def parse_query_with_constraints(query: str) -> str:
|
|
77
|
+
"""Parse a query with fff-style constraints.
|
|
78
|
+
|
|
79
|
+
Shows how the query is decomposed into the main search term
|
|
80
|
+
and any constraints.
|
|
81
|
+
|
|
82
|
+
Supported constraints:
|
|
83
|
+
- git:modified, git:staged, git:deleted, git:renamed, git:untracked, git:ignored
|
|
84
|
+
- !pattern - exclude files matching pattern
|
|
85
|
+
- path/ - filter by directory
|
|
86
|
+
- ./**/*.py - glob patterns
|
|
87
|
+
- *.py - extension filters
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
query: Query string to parse.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Parsed constraint breakdown.
|
|
94
|
+
"""
|
|
95
|
+
from attocode_intel._internal.integrations.context.query_constraints import parse_query_constraints
|
|
96
|
+
|
|
97
|
+
parsed = parse_query_constraints(query)
|
|
98
|
+
|
|
99
|
+
lines = [
|
|
100
|
+
f"Query: '{query}'",
|
|
101
|
+
"\nParsed components:",
|
|
102
|
+
f" Main query: '{parsed.query}'" if parsed.query else " Main query: (empty)",
|
|
103
|
+
f"\n Constraints ({len(parsed.constraints)}):",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
if not parsed.constraints:
|
|
107
|
+
lines.append(" (none)")
|
|
108
|
+
else:
|
|
109
|
+
for i, c in enumerate(parsed.constraints, 1):
|
|
110
|
+
neg_str = " (negated)" if c.negated else ""
|
|
111
|
+
lines.append(f" {i}. [{c.type}] '{c.value}'{neg_str}")
|
|
112
|
+
|
|
113
|
+
lines.append("\nExamples of valid queries:")
|
|
114
|
+
lines.append(' "git:modified *.py" - Find modified Python files')
|
|
115
|
+
lines.append(' "!test/ src/**/*.rs" - Rust files not in test/')
|
|
116
|
+
lines.append(' "git:staged !vendor/" - Staged files except vendor')
|
|
117
|
+
|
|
118
|
+
return "\n".join(lines)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@mcp.tool()
|
|
122
|
+
def filter_files_with_constraints(
|
|
123
|
+
query: str,
|
|
124
|
+
files: str,
|
|
125
|
+
) -> str:
|
|
126
|
+
"""Filter a list of files using query constraints.
|
|
127
|
+
|
|
128
|
+
Takes a query with constraints and a list of files, then returns
|
|
129
|
+
only the files that match all constraints.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
query: Query with constraints (e.g., "git:modified *.py")
|
|
133
|
+
files: Newline-separated list of file paths to filter.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Filtered list of files.
|
|
137
|
+
"""
|
|
138
|
+
from attocode_intel._internal.integrations.context.query_constraints import (
|
|
139
|
+
GitStatus,
|
|
140
|
+
matches_constraints,
|
|
141
|
+
parse_query_constraints,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
file_list = [f.strip() for f in files.splitlines() if f.strip()]
|
|
145
|
+
|
|
146
|
+
if not file_list:
|
|
147
|
+
return "No files provided."
|
|
148
|
+
|
|
149
|
+
parsed = parse_query_constraints(query)
|
|
150
|
+
|
|
151
|
+
if not parsed.constraints:
|
|
152
|
+
return f"No constraints found in query '{query}'."
|
|
153
|
+
|
|
154
|
+
# Get git statuses
|
|
155
|
+
git_statuses_raw = _run_git_status()
|
|
156
|
+
git_statuses: dict[str, GitStatus] = {}
|
|
157
|
+
|
|
158
|
+
status_map = {
|
|
159
|
+
"M": GitStatus.MODIFIED,
|
|
160
|
+
"A": GitStatus.STAGED,
|
|
161
|
+
"D": GitStatus.DELETED,
|
|
162
|
+
"R": GitStatus.RENAMED,
|
|
163
|
+
"?": GitStatus.UNTRACKED,
|
|
164
|
+
"!": GitStatus.IGNORED,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for file_path, status_char in git_statuses_raw.items():
|
|
168
|
+
git_statuses[file_path] = status_map.get(status_char, GitStatus.MODIFIED)
|
|
169
|
+
|
|
170
|
+
# Filter files
|
|
171
|
+
filtered = []
|
|
172
|
+
for file_path in file_list:
|
|
173
|
+
git_status = git_statuses.get(file_path)
|
|
174
|
+
if matches_constraints(file_path, parsed.constraints, git_status):
|
|
175
|
+
filtered.append(file_path)
|
|
176
|
+
|
|
177
|
+
if not filtered:
|
|
178
|
+
return (
|
|
179
|
+
f"No files matched constraints from query '{query}'.\n"
|
|
180
|
+
f"Tried to filter {len(file_list)} files."
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
lines = [
|
|
184
|
+
f"Filtered {len(filtered)} files (from {len(file_list)} input files):",
|
|
185
|
+
f"Query: '{query}'\n",
|
|
186
|
+
]
|
|
187
|
+
lines.extend(filtered)
|
|
188
|
+
|
|
189
|
+
return "\n".join(lines)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@mcp.tool()
|
|
193
|
+
def list_modified_files() -> str:
|
|
194
|
+
"""List all git-modified files in the project.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
List of modified files with their status.
|
|
198
|
+
"""
|
|
199
|
+
project_dir = _get_project_dir()
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
result = subprocess.run(
|
|
203
|
+
["git", "status", "--porcelain"],
|
|
204
|
+
cwd=project_dir,
|
|
205
|
+
capture_output=True,
|
|
206
|
+
text=True,
|
|
207
|
+
timeout=30,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
files_by_status: dict[str, list[str]] = {}
|
|
211
|
+
|
|
212
|
+
status_map = {
|
|
213
|
+
"M": "modified",
|
|
214
|
+
"A": "staged",
|
|
215
|
+
"D": "deleted",
|
|
216
|
+
"R": "renamed",
|
|
217
|
+
"?": "untracked",
|
|
218
|
+
"!": "ignored",
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for line in result.stdout.splitlines():
|
|
222
|
+
if len(line) < 3:
|
|
223
|
+
continue
|
|
224
|
+
|
|
225
|
+
x_status = line[0] # index (staging area)
|
|
226
|
+
y_status = line[1] # worktree
|
|
227
|
+
file_path = line[3:].strip()
|
|
228
|
+
|
|
229
|
+
# Add file under each applicable status
|
|
230
|
+
statuses_to_add: list[str] = []
|
|
231
|
+
if x_status not in (" ", "?", "!"):
|
|
232
|
+
statuses_to_add.append(
|
|
233
|
+
status_map.get(x_status, f"unknown({x_status})")
|
|
234
|
+
)
|
|
235
|
+
if y_status not in (" ", "?", "!"):
|
|
236
|
+
name = status_map.get(y_status, f"unknown({y_status})")
|
|
237
|
+
if name not in statuses_to_add:
|
|
238
|
+
statuses_to_add.append(name)
|
|
239
|
+
# Handle untracked/ignored (both columns are the same char)
|
|
240
|
+
if x_status == "?":
|
|
241
|
+
statuses_to_add.append("untracked")
|
|
242
|
+
elif x_status == "!":
|
|
243
|
+
statuses_to_add.append("ignored")
|
|
244
|
+
|
|
245
|
+
for status_name in statuses_to_add:
|
|
246
|
+
if status_name not in files_by_status:
|
|
247
|
+
files_by_status[status_name] = []
|
|
248
|
+
files_by_status[status_name].append(file_path)
|
|
249
|
+
|
|
250
|
+
if not files_by_status:
|
|
251
|
+
return "No modified files."
|
|
252
|
+
|
|
253
|
+
lines = ["Modified files:"]
|
|
254
|
+
for status_name, files in files_by_status.items():
|
|
255
|
+
lines.append(f"\n{status_name.upper()}:")
|
|
256
|
+
for f in sorted(files):
|
|
257
|
+
lines.append(f" {f}")
|
|
258
|
+
|
|
259
|
+
return "\n".join(lines)
|
|
260
|
+
|
|
261
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
|
|
262
|
+
return f"Error running git: {e}"
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Query history MCP tools for tracking search-result selections.
|
|
2
|
+
|
|
3
|
+
Tracks which files are selected after which queries, enabling
|
|
4
|
+
combo boosting to prioritize commonly co-occurring results.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from attocode_intel._shared import _get_project_dir, mcp
|
|
13
|
+
from attocode_intel.tools.pin_tools import pin_stamped
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from attocode_intel._internal.integrations.context.query_history import QueryHistoryTracker
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_query_tracker() -> QueryHistoryTracker:
|
|
20
|
+
"""Get the query history tracker instance."""
|
|
21
|
+
from attocode_intel._internal.integrations.context.query_history import get_query_tracker
|
|
22
|
+
|
|
23
|
+
project_dir = _get_project_dir()
|
|
24
|
+
db_path = Path(project_dir) / ".attocode" / "query_history"
|
|
25
|
+
return get_query_tracker(db_path=db_path)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@mcp.tool()
|
|
29
|
+
def track_query_result(query: str, file_path: str) -> str:
|
|
30
|
+
"""Track that a user selected a file after searching.
|
|
31
|
+
|
|
32
|
+
Call this whenever a user searches for something and then opens
|
|
33
|
+
a file from the results. This builds up combo boosting to help
|
|
34
|
+
prioritize commonly selected files for future searches.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
query: The search query that led to the selection.
|
|
38
|
+
file_path: The file that was selected/opened.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Confirmation message.
|
|
42
|
+
"""
|
|
43
|
+
tracker = _get_query_tracker()
|
|
44
|
+
tracker.track_selection(query, file_path)
|
|
45
|
+
|
|
46
|
+
# Check if this triggers combo boost
|
|
47
|
+
boost = tracker.get_combo_boost(query, file_path)
|
|
48
|
+
if boost > 0:
|
|
49
|
+
return (
|
|
50
|
+
f"Tracked: '{query}' -> {file_path}\n"
|
|
51
|
+
f"Combo boost activated: +{boost:.0f}"
|
|
52
|
+
)
|
|
53
|
+
return f"Tracked: '{query}' -> {file_path}"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@mcp.tool()
|
|
57
|
+
def get_query_combo_boost(query: str, file_path: str) -> str:
|
|
58
|
+
"""Get the combo boost score for a query+file pair.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
query: The search query.
|
|
62
|
+
file_path: The file path to check.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Combo boost score and metadata.
|
|
66
|
+
"""
|
|
67
|
+
tracker = _get_query_tracker()
|
|
68
|
+
boost = tracker.get_combo_boost(query, file_path)
|
|
69
|
+
|
|
70
|
+
if boost == 0:
|
|
71
|
+
return (
|
|
72
|
+
f"No combo boost for '{query}' -> {file_path}\n"
|
|
73
|
+
f"(Need 3+ selections to activate boost)"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
f"Combo boost: +{boost:.0f}\n"
|
|
78
|
+
f"Query: '{query}'\n"
|
|
79
|
+
f"File: {file_path}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@mcp.tool()
|
|
84
|
+
@pin_stamped
|
|
85
|
+
def get_top_results_for_query(query: str, limit: int = 10) -> str:
|
|
86
|
+
"""Get the top files commonly selected for a query.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
query: The search query.
|
|
90
|
+
limit: Maximum number of results to return.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Top files for this query with selection counts.
|
|
94
|
+
"""
|
|
95
|
+
tracker = _get_query_tracker()
|
|
96
|
+
top_files = tracker.get_top_files_for_query(query, limit=limit)
|
|
97
|
+
|
|
98
|
+
if not top_files:
|
|
99
|
+
return f"No history for query: '{query}'"
|
|
100
|
+
|
|
101
|
+
lines = [
|
|
102
|
+
f"Top results for: '{query}'",
|
|
103
|
+
f"(Based on {limit} historical selections)\n",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
for i, (file_path, count, combo_score) in enumerate(top_files, 1):
|
|
107
|
+
boost_str = f" (+{combo_score:.0f} boost)" if combo_score > 0 else ""
|
|
108
|
+
lines.append(f"{i}. [{count}x] {file_path}{boost_str}")
|
|
109
|
+
|
|
110
|
+
return "\n".join(lines)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@mcp.tool()
|
|
114
|
+
def get_query_history_stats() -> str:
|
|
115
|
+
"""Get overall query history statistics.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Statistics about tracked queries and selections.
|
|
119
|
+
"""
|
|
120
|
+
tracker = _get_query_tracker()
|
|
121
|
+
stats = tracker.get_stats()
|
|
122
|
+
|
|
123
|
+
lines = [
|
|
124
|
+
"Query History Statistics",
|
|
125
|
+
"=" * 40,
|
|
126
|
+
f"Total selections tracked: {stats.total_selections}",
|
|
127
|
+
f"Total query entries: {stats.total_queries}",
|
|
128
|
+
f"Unique queries: {stats.unique_queries}",
|
|
129
|
+
f"Unique files: {stats.unique_files}",
|
|
130
|
+
f"Files with combo boost: {stats.combo_boosts}",
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
return "\n".join(lines)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@mcp.tool()
|
|
137
|
+
def clear_query_history(query: str | None = None, file_path: str | None = None) -> str:
|
|
138
|
+
"""Clear query history.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
query: Optional specific query to clear. If None, clears all.
|
|
142
|
+
file_path: Optional specific file to clear (requires query).
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Confirmation message.
|
|
146
|
+
"""
|
|
147
|
+
tracker = _get_query_tracker()
|
|
148
|
+
|
|
149
|
+
if query is None:
|
|
150
|
+
tracker.clear()
|
|
151
|
+
return "All query history cleared."
|
|
152
|
+
|
|
153
|
+
count = tracker.clear(query, file_path)
|
|
154
|
+
if count > 0:
|
|
155
|
+
if file_path:
|
|
156
|
+
return f"Cleared history for '{query}' -> {file_path}"
|
|
157
|
+
return f"Cleared {count} entries for query: '{query}'"
|
|
158
|
+
return f"No history found for query: '{query}'"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Readiness report tool for the code-intel MCP server.
|
|
2
|
+
|
|
3
|
+
Tools: readiness_report.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from attocode_intel._shared import (
|
|
9
|
+
_get_service,
|
|
10
|
+
mcp,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@mcp.tool()
|
|
15
|
+
def readiness_report(
|
|
16
|
+
phases: list[int] | None = None,
|
|
17
|
+
scope: str = "",
|
|
18
|
+
tracer_bullets: bool = True,
|
|
19
|
+
min_severity: str = "info",
|
|
20
|
+
) -> str:
|
|
21
|
+
"""Run a comprehensive readiness audit of the codebase.
|
|
22
|
+
|
|
23
|
+
Executes 8 analysis phases covering baseline health, API completeness,
|
|
24
|
+
frontend flows, data layer, test coverage, security, observability,
|
|
25
|
+
and deployability. Orchestrates existing tools (security_scan, dead_code,
|
|
26
|
+
hotspots, conventions, etc.) plus new phase-specific analysis.
|
|
27
|
+
|
|
28
|
+
Returns severity-tagged findings with file:line evidence and an overall
|
|
29
|
+
readiness grade (A-F).
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
phases: List of phase numbers to run (0-7). None = all phases.
|
|
33
|
+
0=Baseline, 1=API/Logic, 2=Frontend, 3=Data, 4=Tests,
|
|
34
|
+
5=Security, 6=Observability, 7=Deployability.
|
|
35
|
+
scope: Restrict analysis to files under this directory prefix.
|
|
36
|
+
tracer_bullets: Whether to run tracer bullet analysis (default True).
|
|
37
|
+
min_severity: Minimum severity to include: "pass", "info", "warning", "critical".
|
|
38
|
+
"""
|
|
39
|
+
return _get_service().readiness_report(
|
|
40
|
+
phases=phases,
|
|
41
|
+
scope=scope,
|
|
42
|
+
tracer_bullets=tracer_bullets,
|
|
43
|
+
min_severity=min_severity,
|
|
44
|
+
)
|