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,263 @@
|
|
|
1
|
+
"""Cross-mode search suggestions.
|
|
2
|
+
|
|
3
|
+
When one search mode returns no results, automatically query the other mode
|
|
4
|
+
and suggest those results:
|
|
5
|
+
- File search with no results → shows grep/content match suggestions
|
|
6
|
+
- Grep with no results → shows file name suggestions
|
|
7
|
+
|
|
8
|
+
This is a "did you mean" feature for search.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Data structures
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class SearchSuggestion:
|
|
24
|
+
"""A suggested result from the alternate search mode."""
|
|
25
|
+
file_path: str
|
|
26
|
+
line_number: int | None # None for file suggestions
|
|
27
|
+
matched_text: str | None # The text that matched (for grep suggestions)
|
|
28
|
+
score: float # Relevance score
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# Cross-mode search
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def suggest_grep_for_filename_query(
|
|
37
|
+
query: str,
|
|
38
|
+
project_dir: str,
|
|
39
|
+
max_suggestions: int = 10,
|
|
40
|
+
) -> list[SearchSuggestion]:
|
|
41
|
+
"""When filename search finds nothing, suggest grep matches.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
query: The original filename search query.
|
|
45
|
+
project_dir: Project root directory.
|
|
46
|
+
max_suggestions: Maximum suggestions to return.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
List of grep-based suggestions.
|
|
50
|
+
"""
|
|
51
|
+
suggestions: list[SearchSuggestion] = []
|
|
52
|
+
|
|
53
|
+
# Use simple grep-like search for the query in file contents
|
|
54
|
+
pattern = re.escape(query)
|
|
55
|
+
try:
|
|
56
|
+
regex = re.compile(pattern, re.IGNORECASE)
|
|
57
|
+
except re.error:
|
|
58
|
+
# If query is not a valid regex, do literal search
|
|
59
|
+
pattern_literal = query.lower()
|
|
60
|
+
regex = None
|
|
61
|
+
|
|
62
|
+
project_path = Path(project_dir)
|
|
63
|
+
|
|
64
|
+
# Set up gitignore filtering
|
|
65
|
+
try:
|
|
66
|
+
from attocode_intel._internal.integrations.utilities.ignore import IgnoreManager
|
|
67
|
+
|
|
68
|
+
ignore_mgr = IgnoreManager(root=project_path)
|
|
69
|
+
except ImportError:
|
|
70
|
+
ignore_mgr = None
|
|
71
|
+
|
|
72
|
+
# Walk files and search for matches
|
|
73
|
+
for file in project_path.rglob("*"):
|
|
74
|
+
if not file.is_file() or file.name.startswith("."):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
rel_path = str(file.relative_to(project_path))
|
|
79
|
+
except ValueError:
|
|
80
|
+
rel_path = str(file)
|
|
81
|
+
|
|
82
|
+
if ignore_mgr is not None and ignore_mgr.is_ignored(rel_path):
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
# Skip binary and large files
|
|
86
|
+
try:
|
|
87
|
+
if file.stat().st_size > 1_000_000: # 1MB
|
|
88
|
+
continue
|
|
89
|
+
content = file.read_text(encoding="utf-8", errors="strict")
|
|
90
|
+
except (OSError, UnicodeDecodeError):
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
# Search line by line
|
|
94
|
+
for i, line in enumerate(content.splitlines(), 1):
|
|
95
|
+
match = regex.search(line) if regex else pattern_literal in line.lower()
|
|
96
|
+
|
|
97
|
+
if match:
|
|
98
|
+
suggestions.append(SearchSuggestion(
|
|
99
|
+
file_path=rel_path,
|
|
100
|
+
line_number=i,
|
|
101
|
+
matched_text=line.strip(),
|
|
102
|
+
score=100.0,
|
|
103
|
+
))
|
|
104
|
+
|
|
105
|
+
if len(suggestions) >= max_suggestions:
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
if len(suggestions) >= max_suggestions:
|
|
109
|
+
break
|
|
110
|
+
|
|
111
|
+
return suggestions[:max_suggestions]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def suggest_files_for_grep_query(
|
|
115
|
+
query: str,
|
|
116
|
+
project_dir: str,
|
|
117
|
+
max_suggestions: int = 10,
|
|
118
|
+
) -> list[SearchSuggestion]:
|
|
119
|
+
"""When grep finds nothing, suggest files with matching names.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
query: The original grep search query.
|
|
123
|
+
project_dir: Project root directory.
|
|
124
|
+
max_suggestions: Maximum suggestions to return.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
List of filename-based suggestions.
|
|
128
|
+
"""
|
|
129
|
+
suggestions: list[SearchSuggestion] = []
|
|
130
|
+
|
|
131
|
+
# Parse query - could be a regex or plain text
|
|
132
|
+
# Extract meaningful parts from the query
|
|
133
|
+
# Remove regex special chars for literal matching
|
|
134
|
+
literal_query = re.sub(r'[\^\$\.\|\(\)\[\]\+\*\?\\]', '', query)
|
|
135
|
+
literal_query = literal_query.strip()
|
|
136
|
+
|
|
137
|
+
if not literal_query:
|
|
138
|
+
return suggestions
|
|
139
|
+
|
|
140
|
+
project_path = Path(project_dir)
|
|
141
|
+
|
|
142
|
+
# Set up gitignore filtering
|
|
143
|
+
try:
|
|
144
|
+
from attocode_intel._internal.integrations.utilities.ignore import IgnoreManager
|
|
145
|
+
|
|
146
|
+
ignore_mgr = IgnoreManager(root=project_path)
|
|
147
|
+
except ImportError:
|
|
148
|
+
ignore_mgr = None
|
|
149
|
+
|
|
150
|
+
# Walk files and look for name matches
|
|
151
|
+
for file in project_path.rglob("*"):
|
|
152
|
+
if not file.is_file() or file.name.startswith("."):
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
if ignore_mgr is not None:
|
|
156
|
+
try:
|
|
157
|
+
rel = str(file.relative_to(project_path))
|
|
158
|
+
except ValueError:
|
|
159
|
+
continue
|
|
160
|
+
if ignore_mgr.is_ignored(rel):
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
name = file.name.lower()
|
|
164
|
+
name_without_ext = file.stem.lower()
|
|
165
|
+
|
|
166
|
+
# Check if query matches filename
|
|
167
|
+
score = 0.0
|
|
168
|
+
if literal_query in name:
|
|
169
|
+
score = 50.0 + (len(literal_query) / len(name)) * 50.0
|
|
170
|
+
elif literal_query in name_without_ext:
|
|
171
|
+
score = 30.0 + (len(literal_query) / len(name_without_ext)) * 40.0
|
|
172
|
+
else:
|
|
173
|
+
# Fuzzy-ish matching: check if all chars appear in order
|
|
174
|
+
if _chars_in_order(literal_query, name_without_ext):
|
|
175
|
+
score = 20.0
|
|
176
|
+
|
|
177
|
+
if score > 20.0:
|
|
178
|
+
try:
|
|
179
|
+
rel_path = str(file.relative_to(project_path))
|
|
180
|
+
except ValueError:
|
|
181
|
+
rel_path = str(file)
|
|
182
|
+
|
|
183
|
+
suggestions.append(SearchSuggestion(
|
|
184
|
+
file_path=rel_path,
|
|
185
|
+
line_number=None,
|
|
186
|
+
matched_text=file.name,
|
|
187
|
+
score=score,
|
|
188
|
+
))
|
|
189
|
+
|
|
190
|
+
# Sort by score descending
|
|
191
|
+
suggestions.sort(key=lambda s: s.score, reverse=True)
|
|
192
|
+
return suggestions[:max_suggestions]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _chars_in_order(pattern: str, text: str) -> bool:
|
|
196
|
+
"""Check if all chars in pattern appear in text in order.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
pattern: Characters to find.
|
|
200
|
+
text: Text to search in.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
True if all pattern chars found in order.
|
|
204
|
+
"""
|
|
205
|
+
pi = 0
|
|
206
|
+
for c in text:
|
|
207
|
+
if pi >= len(pattern):
|
|
208
|
+
break
|
|
209
|
+
if c == pattern[pi]:
|
|
210
|
+
pi += 1
|
|
211
|
+
return pi >= len(pattern)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# ---------------------------------------------------------------------------
|
|
215
|
+
# Unified cross-mode search
|
|
216
|
+
# ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class CrossModeSearcher:
|
|
220
|
+
"""Provides cross-mode search suggestions."""
|
|
221
|
+
|
|
222
|
+
def __init__(self, project_dir: str) -> None:
|
|
223
|
+
self.project_dir = project_dir
|
|
224
|
+
|
|
225
|
+
def suggest_content_matches(
|
|
226
|
+
self,
|
|
227
|
+
query: str,
|
|
228
|
+
max_suggestions: int = 10,
|
|
229
|
+
) -> list[SearchSuggestion]:
|
|
230
|
+
"""Suggest content/grep matches when file-name search fails.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
query: The file search query.
|
|
234
|
+
max_suggestions: Maximum suggestions.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Content-based suggestions (grep results).
|
|
238
|
+
"""
|
|
239
|
+
return suggest_grep_for_filename_query(
|
|
240
|
+
query=query,
|
|
241
|
+
project_dir=self.project_dir,
|
|
242
|
+
max_suggestions=max_suggestions,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def suggest_filename_matches(
|
|
246
|
+
self,
|
|
247
|
+
query: str,
|
|
248
|
+
max_suggestions: int = 10,
|
|
249
|
+
) -> list[SearchSuggestion]:
|
|
250
|
+
"""Suggest filename matches when grep/content search fails.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
query: The grep query.
|
|
254
|
+
max_suggestions: Maximum suggestions.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Filename-based suggestions.
|
|
258
|
+
"""
|
|
259
|
+
return suggest_files_for_grep_query(
|
|
260
|
+
query=query,
|
|
261
|
+
project_dir=self.project_dir,
|
|
262
|
+
max_suggestions=max_suggestions,
|
|
263
|
+
)
|