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.
Files changed (345) hide show
  1. attocode_code_intel-0.1.0.dist-info/METADATA +221 -0
  2. attocode_code_intel-0.1.0.dist-info/RECORD +345 -0
  3. attocode_code_intel-0.1.0.dist-info/WHEEL +4 -0
  4. attocode_code_intel-0.1.0.dist-info/entry_points.txt +2 -0
  5. attocode_code_intel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. attocode_intel/GUIDELINES.md +432 -0
  7. attocode_intel/__init__.py +52 -0
  8. attocode_intel/_internal/__init__.py +1 -0
  9. attocode_intel/_internal/errors/__init__.py +126 -0
  10. attocode_intel/_internal/integrations/__init__.py +1 -0
  11. attocode_intel/_internal/integrations/context/__init__.py +1 -0
  12. attocode_intel/_internal/integrations/context/architecture_drift.py +354 -0
  13. attocode_intel/_internal/integrations/context/ast_chunker.py +202 -0
  14. attocode_intel/_internal/integrations/context/ast_service.py +1483 -0
  15. attocode_intel/_internal/integrations/context/blob_oid.py +423 -0
  16. attocode_intel/_internal/integrations/context/cache_manifest.py +221 -0
  17. attocode_intel/_internal/integrations/context/cas.py +466 -0
  18. attocode_intel/_internal/integrations/context/code_analyzer.py +528 -0
  19. attocode_intel/_internal/integrations/context/codebase_ast.py +1461 -0
  20. attocode_intel/_internal/integrations/context/codebase_context.py +1736 -0
  21. attocode_intel/_internal/integrations/context/cross_mode.py +263 -0
  22. attocode_intel/_internal/integrations/context/cross_references.py +616 -0
  23. attocode_intel/_internal/integrations/context/embedding_rotation.py +597 -0
  24. attocode_intel/_internal/integrations/context/embeddings.py +282 -0
  25. attocode_intel/_internal/integrations/context/frecency.py +427 -0
  26. attocode_intel/_internal/integrations/context/fuzzy.py +320 -0
  27. attocode_intel/_internal/integrations/context/hierarchical_explorer.py +331 -0
  28. attocode_intel/_internal/integrations/context/hydration.py +90 -0
  29. attocode_intel/_internal/integrations/context/index_repair.py +87 -0
  30. attocode_intel/_internal/integrations/context/index_store.py +586 -0
  31. attocode_intel/_internal/integrations/context/memory_store.py +602 -0
  32. attocode_intel/_internal/integrations/context/module_resolution.py +52 -0
  33. attocode_intel/_internal/integrations/context/query_constraints.py +361 -0
  34. attocode_intel/_internal/integrations/context/query_history.py +417 -0
  35. attocode_intel/_internal/integrations/context/reranker.py +351 -0
  36. attocode_intel/_internal/integrations/context/semantic_search.py +2931 -0
  37. attocode_intel/_internal/integrations/context/syntax_evidence.py +94 -0
  38. attocode_intel/_internal/integrations/context/systemone_ranker.py +245 -0
  39. attocode_intel/_internal/integrations/context/temporal_coupling.py +411 -0
  40. attocode_intel/_internal/integrations/context/trigram_index.py +820 -0
  41. attocode_intel/_internal/integrations/context/trigram_regex.py +171 -0
  42. attocode_intel/_internal/integrations/context/ts_parser.py +2199 -0
  43. attocode_intel/_internal/integrations/context/vector_store.py +795 -0
  44. attocode_intel/_internal/integrations/feature_flags/__init__.py +357 -0
  45. attocode_intel/_internal/integrations/lsp/__init__.py +1 -0
  46. attocode_intel/_internal/integrations/lsp/client.py +1140 -0
  47. attocode_intel/_internal/integrations/mcp/__init__.py +1 -0
  48. attocode_intel/_internal/integrations/mcp/client.py +253 -0
  49. attocode_intel/_internal/integrations/security/__init__.py +1 -0
  50. attocode_intel/_internal/integrations/security/custom_rules.py +176 -0
  51. attocode_intel/_internal/integrations/security/dataflow.py +522 -0
  52. attocode_intel/_internal/integrations/security/dependency_audit.py +303 -0
  53. attocode_intel/_internal/integrations/security/matcher.py +44 -0
  54. attocode_intel/_internal/integrations/security/patterns.py +724 -0
  55. attocode_intel/_internal/integrations/security/scanner.py +351 -0
  56. attocode_intel/_internal/integrations/utilities/__init__.py +1 -0
  57. attocode_intel/_internal/integrations/utilities/ignore.py +64 -0
  58. attocode_intel/_internal/integrations/utilities/token_estimate.py +51 -0
  59. attocode_intel/_internal/tricks/__init__.py +1 -0
  60. attocode_intel/_internal/tricks/recursive_context.py +357 -0
  61. attocode_intel/_internal/types/__init__.py +1 -0
  62. attocode_intel/_internal/types/events.py +289 -0
  63. attocode_intel/_internal/types/severity.py +15 -0
  64. attocode_intel/_shared.py +341 -0
  65. attocode_intel/analysis_coverage.py +56 -0
  66. attocode_intel/api/__init__.py +1 -0
  67. attocode_intel/api/access.py +32 -0
  68. attocode_intel/api/app.py +247 -0
  69. attocode_intel/api/auth/__init__.py +128 -0
  70. attocode_intel/api/auth/api_keys.py +61 -0
  71. attocode_intel/api/auth/context.py +34 -0
  72. attocode_intel/api/auth/jwt.py +66 -0
  73. attocode_intel/api/auth/oauth.py +146 -0
  74. attocode_intel/api/auth/passwords.py +15 -0
  75. attocode_intel/api/deps.py +351 -0
  76. attocode_intel/api/knowledge_bridge.py +13 -0
  77. attocode_intel/api/mcp_transport.py +51 -0
  78. attocode_intel/api/middleware.py +411 -0
  79. attocode_intel/api/models.py +698 -0
  80. attocode_intel/api/providers/__init__.py +1 -0
  81. attocode_intel/api/providers/db_provider.py +1106 -0
  82. attocode_intel/api/providers/local_provider.py +476 -0
  83. attocode_intel/api/providers/remote_provider.py +740 -0
  84. attocode_intel/api/repository_source.py +21 -0
  85. attocode_intel/api/routes/__init__.py +1 -0
  86. attocode_intel/api/routes/_pin_helper.py +158 -0
  87. attocode_intel/api/routes/activity.py +148 -0
  88. attocode_intel/api/routes/adr.py +102 -0
  89. attocode_intel/api/routes/analysis.py +515 -0
  90. attocode_intel/api/routes/api_keys.py +170 -0
  91. attocode_intel/api/routes/auth.py +428 -0
  92. attocode_intel/api/routes/branches.py +262 -0
  93. attocode_intel/api/routes/cross_repo_search.py +198 -0
  94. attocode_intel/api/routes/embeddings.py +386 -0
  95. attocode_intel/api/routes/files.py +132 -0
  96. attocode_intel/api/routes/files_v2.py +388 -0
  97. attocode_intel/api/routes/gc.py +322 -0
  98. attocode_intel/api/routes/git_v2.py +433 -0
  99. attocode_intel/api/routes/graph.py +174 -0
  100. attocode_intel/api/routes/graph_viz.py +160 -0
  101. attocode_intel/api/routes/health.py +62 -0
  102. attocode_intel/api/routes/history.py +175 -0
  103. attocode_intel/api/routes/jobs.py +84 -0
  104. attocode_intel/api/routes/learning.py +205 -0
  105. attocode_intel/api/routes/lsp.py +272 -0
  106. attocode_intel/api/routes/notify.py +623 -0
  107. attocode_intel/api/routes/operations.py +27 -0
  108. attocode_intel/api/routes/orgs.py +924 -0
  109. attocode_intel/api/routes/preferences.py +100 -0
  110. attocode_intel/api/routes/presence.py +42 -0
  111. attocode_intel/api/routes/projects.py +116 -0
  112. attocode_intel/api/routes/repos.py +47 -0
  113. attocode_intel/api/routes/search.py +180 -0
  114. attocode_intel/api/routes/snapshots.py +614 -0
  115. attocode_intel/api/routes/webhooks.py +313 -0
  116. attocode_intel/api/routes/websocket.py +116 -0
  117. attocode_intel/api/static/graph.html +239 -0
  118. attocode_intel/api/utils.py +96 -0
  119. attocode_intel/artifacts/__init__.py +45 -0
  120. attocode_intel/artifacts/hashing.py +230 -0
  121. attocode_intel/artifacts/provenance.py +190 -0
  122. attocode_intel/artifacts/retrieval_pin.py +159 -0
  123. attocode_intel/audit.py +59 -0
  124. attocode_intel/bug_finder.py +206 -0
  125. attocode_intel/bundle.py +80 -0
  126. attocode_intel/catalog.py +242 -0
  127. attocode_intel/change_review.py +75 -0
  128. attocode_intel/cli.py +3062 -0
  129. attocode_intel/community.py +63 -0
  130. attocode_intel/confidence/__init__.py +144 -0
  131. attocode_intel/confidence/jev.py +234 -0
  132. attocode_intel/confidence/llm.py +330 -0
  133. attocode_intel/confidence/redact.py +44 -0
  134. attocode_intel/confidence/settings.py +74 -0
  135. attocode_intel/config.py +256 -0
  136. attocode_intel/crypto.py +31 -0
  137. attocode_intel/db/__init__.py +7 -0
  138. attocode_intel/db/base.py +33 -0
  139. attocode_intel/db/engine.py +61 -0
  140. attocode_intel/db/models.py +529 -0
  141. attocode_intel/entrypoint.py +185 -0
  142. attocode_intel/focused_evidence.py +130 -0
  143. attocode_intel/freshness.py +154 -0
  144. attocode_intel/gateway.py +734 -0
  145. attocode_intel/git/__init__.py +7 -0
  146. attocode_intel/git/credentials.py +75 -0
  147. attocode_intel/git/manager.py +493 -0
  148. attocode_intel/git/models.py +96 -0
  149. attocode_intel/git/storage.py +98 -0
  150. attocode_intel/graph_query_parser.py +761 -0
  151. attocode_intel/helpers.py +712 -0
  152. attocode_intel/indexing/__init__.py +29 -0
  153. attocode_intel/indexing/debouncer.py +136 -0
  154. attocode_intel/indexing/delta_indexer.py +151 -0
  155. attocode_intel/indexing/full_indexer.py +323 -0
  156. attocode_intel/indexing/incremental.py +240 -0
  157. attocode_intel/indexing/parser.py +403 -0
  158. attocode_intel/indexing/summarizer.py +264 -0
  159. attocode_intel/installer.py +1896 -0
  160. attocode_intel/knowledge.py +269 -0
  161. attocode_intel/local_knowledge.py +77 -0
  162. attocode_intel/migrations/alembic.ini +36 -0
  163. attocode_intel/migrations/env.py +71 -0
  164. attocode_intel/migrations/script.py.mako +27 -0
  165. attocode_intel/migrations/versions/001_initial_schema.py +219 -0
  166. attocode_intel/migrations/versions/002_branch_version_and_embeddings.py +61 -0
  167. attocode_intel/migrations/versions/003_embedding_uniqueness_and_cascade.py +51 -0
  168. attocode_intel/migrations/versions/004_audit_events.py +38 -0
  169. attocode_intel/migrations/versions/005_user_preferences.py +30 -0
  170. attocode_intel/migrations/versions/006_google_oauth.py +27 -0
  171. attocode_intel/migrations/versions/007_pgvector_embeddings.py +28 -0
  172. attocode_intel/migrations/versions/008_symbol_references.py +37 -0
  173. attocode_intel/migrations/versions/009_webhook_secret_encryption.py +25 -0
  174. attocode_intel/migrations/versions/010_add_commits_table.py +40 -0
  175. attocode_intel/migrations/versions/011_commit_changed_files.py +25 -0
  176. attocode_intel/migrations/versions/012_branch_merged_at.py +25 -0
  177. attocode_intel/migrations/versions/013_revoked_tokens.py +32 -0
  178. attocode_intel/migrations/versions/014_blame_hunks.py +40 -0
  179. attocode_intel/migrations/versions/015_commit_file_stats.py +40 -0
  180. attocode_intel/migrations/versions/016_embedding_provenance_columns.py +179 -0
  181. attocode_intel/migrations/versions/017_provenance_table.py +102 -0
  182. attocode_intel/migrations/versions/018_repo_snapshots.py +178 -0
  183. attocode_intel/migrations/versions/019_symref_caller.py +42 -0
  184. attocode_intel/migrations/versions/020_shared_knowledge.py +29 -0
  185. attocode_intel/onboarding.py +258 -0
  186. attocode_intel/output.py +178 -0
  187. attocode_intel/precision.py +133 -0
  188. attocode_intel/presence.py +160 -0
  189. attocode_intel/probe.py +110 -0
  190. attocode_intel/project_dir.py +97 -0
  191. attocode_intel/pubsub.py +113 -0
  192. attocode_intel/query_ranking.py +242 -0
  193. attocode_intel/readiness.py +972 -0
  194. attocode_intel/redis.py +51 -0
  195. attocode_intel/reference_pages.py +60 -0
  196. attocode_intel/remote.py +131 -0
  197. attocode_intel/repo_ranker.py +246 -0
  198. attocode_intel/request_context.py +82 -0
  199. attocode_intel/rules/__init__.py +28 -0
  200. attocode_intel/rules/ci.py +320 -0
  201. attocode_intel/rules/combinators.py +293 -0
  202. attocode_intel/rules/enricher.py +127 -0
  203. attocode_intel/rules/evolution.py +452 -0
  204. attocode_intel/rules/executor.py +531 -0
  205. attocode_intel/rules/filters/__init__.py +5 -0
  206. attocode_intel/rules/filters/pipeline.py +151 -0
  207. attocode_intel/rules/formatter.py +198 -0
  208. attocode_intel/rules/hygiene.py +242 -0
  209. attocode_intel/rules/importers/__init__.py +1 -0
  210. attocode_intel/rules/importers/semgrep.py +382 -0
  211. attocode_intel/rules/loader.py +292 -0
  212. attocode_intel/rules/marketplace.py +375 -0
  213. attocode_intel/rules/metavar.py +181 -0
  214. attocode_intel/rules/model.py +317 -0
  215. attocode_intel/rules/packs/__init__.py +1 -0
  216. attocode_intel/rules/packs/community/_license_templates/Apache-2.0.txt +176 -0
  217. attocode_intel/rules/packs/community/_license_templates/MIT.txt +21 -0
  218. attocode_intel/rules/packs/community/bandit-python/LICENSE +176 -0
  219. attocode_intel/rules/packs/community/bandit-python/NOTICE +13 -0
  220. attocode_intel/rules/packs/community/bandit-python/PORTING.md +23 -0
  221. attocode_intel/rules/packs/community/bandit-python/manifest.yaml +14 -0
  222. attocode_intel/rules/packs/community/bandit-python/rules/B105-hardcoded-password.yaml +23 -0
  223. attocode_intel/rules/packs/community/bandit-python/rules/B303-weak-md5.yaml +21 -0
  224. attocode_intel/rules/packs/community/bandit-python/rules/B304-weak-des.yaml +20 -0
  225. attocode_intel/rules/packs/community/bandit-python/rules/B307-use-of-eval.yaml +22 -0
  226. attocode_intel/rules/packs/community/bandit-python/rules/B324-weak-sha1.yaml +21 -0
  227. attocode_intel/rules/packs/community/bandit-python/rules/B501-no-cert-validation.yaml +23 -0
  228. attocode_intel/rules/packs/community/bandit-python/rules/B602-shell-true.yaml +22 -0
  229. attocode_intel/rules/packs/community/bandit-python/rules/B608-sqli.yaml +22 -0
  230. attocode_intel/rules/packs/community/eslint-typescript/LICENSE +21 -0
  231. attocode_intel/rules/packs/community/eslint-typescript/NOTICE +13 -0
  232. attocode_intel/rules/packs/community/eslint-typescript/PORTING.md +23 -0
  233. attocode_intel/rules/packs/community/eslint-typescript/manifest.yaml +14 -0
  234. attocode_intel/rules/packs/community/eslint-typescript/rules/eqeqeq.yaml +20 -0
  235. attocode_intel/rules/packs/community/eslint-typescript/rules/no-eval.yaml +22 -0
  236. attocode_intel/rules/packs/community/eslint-typescript/rules/no-implied-eval.yaml +20 -0
  237. attocode_intel/rules/packs/community/eslint-typescript/rules/no-new-func.yaml +21 -0
  238. attocode_intel/rules/packs/community/eslint-typescript/rules/no-throw-literal.yaml +19 -0
  239. attocode_intel/rules/packs/community/eslint-typescript/rules/no-var.yaml +20 -0
  240. attocode_intel/rules/packs/community/eslint-typescript/rules/no-with.yaml +18 -0
  241. attocode_intel/rules/packs/community/gosec-go/LICENSE +176 -0
  242. attocode_intel/rules/packs/community/gosec-go/NOTICE +13 -0
  243. attocode_intel/rules/packs/community/gosec-go/PORTING.md +23 -0
  244. attocode_intel/rules/packs/community/gosec-go/manifest.yaml +14 -0
  245. attocode_intel/rules/packs/community/gosec-go/rules/G101-hardcoded-credentials.yaml +22 -0
  246. attocode_intel/rules/packs/community/gosec-go/rules/G102-bind-all-interfaces.yaml +21 -0
  247. attocode_intel/rules/packs/community/gosec-go/rules/G103-unsafe-block.yaml +21 -0
  248. attocode_intel/rules/packs/community/gosec-go/rules/G201-sql-format.yaml +21 -0
  249. attocode_intel/rules/packs/community/gosec-go/rules/G304-file-inclusion.yaml +23 -0
  250. attocode_intel/rules/packs/community/gosec-go/rules/G401-weak-cipher.yaml +20 -0
  251. attocode_intel/rules/packs/community/gosec-go/rules/G402-tls-min-version.yaml +20 -0
  252. attocode_intel/rules/packs/community/gosec-go/rules/G501-md5-import.yaml +21 -0
  253. attocode_intel/rules/packs/examples/cpp/manifest.yaml +4 -0
  254. attocode_intel/rules/packs/examples/cpp/rules/security.yaml +90 -0
  255. attocode_intel/rules/packs/examples/cpp/rules/structural.yaml +42 -0
  256. attocode_intel/rules/packs/examples/csharp/manifest.yaml +4 -0
  257. attocode_intel/rules/packs/examples/csharp/rules/security.yaml +69 -0
  258. attocode_intel/rules/packs/examples/csharp/rules/structural.yaml +42 -0
  259. attocode_intel/rules/packs/examples/go/manifest.yaml +4 -0
  260. attocode_intel/rules/packs/examples/go/rules/performance.yaml +136 -0
  261. attocode_intel/rules/packs/examples/go/rules/security.yaml +48 -0
  262. attocode_intel/rules/packs/examples/go/rules/structural.yaml +58 -0
  263. attocode_intel/rules/packs/examples/go/taint/sanitizers.yaml +24 -0
  264. attocode_intel/rules/packs/examples/go/taint/sinks.yaml +51 -0
  265. attocode_intel/rules/packs/examples/go/taint/sources.yaml +26 -0
  266. attocode_intel/rules/packs/examples/java/manifest.yaml +4 -0
  267. attocode_intel/rules/packs/examples/java/rules/correctness.yaml +55 -0
  268. attocode_intel/rules/packs/examples/java/rules/structural.yaml +48 -0
  269. attocode_intel/rules/packs/examples/java/taint/sinks.yaml +37 -0
  270. attocode_intel/rules/packs/examples/java/taint/sources.yaml +21 -0
  271. attocode_intel/rules/packs/examples/kotlin/manifest.yaml +4 -0
  272. attocode_intel/rules/packs/examples/kotlin/rules/correctness.yaml +50 -0
  273. attocode_intel/rules/packs/examples/kotlin/rules/structural.yaml +38 -0
  274. attocode_intel/rules/packs/examples/php/manifest.yaml +4 -0
  275. attocode_intel/rules/packs/examples/php/rules/security.yaml +66 -0
  276. attocode_intel/rules/packs/examples/php/rules/structural.yaml +41 -0
  277. attocode_intel/rules/packs/examples/python/manifest.yaml +4 -0
  278. attocode_intel/rules/packs/examples/python/rules/performance.yaml +90 -0
  279. attocode_intel/rules/packs/examples/python/rules/structural.yaml +55 -0
  280. attocode_intel/rules/packs/examples/ruby/manifest.yaml +4 -0
  281. attocode_intel/rules/packs/examples/ruby/rules/security.yaml +73 -0
  282. attocode_intel/rules/packs/examples/ruby/rules/structural.yaml +41 -0
  283. attocode_intel/rules/packs/examples/rust/manifest.yaml +4 -0
  284. attocode_intel/rules/packs/examples/rust/rules/correctness.yaml +51 -0
  285. attocode_intel/rules/packs/examples/rust/rules/structural.yaml +49 -0
  286. attocode_intel/rules/packs/examples/typescript/manifest.yaml +4 -0
  287. attocode_intel/rules/packs/examples/typescript/rules/performance.yaml +70 -0
  288. attocode_intel/rules/packs/examples/typescript/rules/structural.yaml +48 -0
  289. attocode_intel/rules/packs/pack_loader.py +244 -0
  290. attocode_intel/rules/plugins/__init__.py +1 -0
  291. attocode_intel/rules/plugins/plugin_loader.py +132 -0
  292. attocode_intel/rules/profiling.py +332 -0
  293. attocode_intel/rules/registry.py +213 -0
  294. attocode_intel/rules/sarif.py +187 -0
  295. attocode_intel/rules/synthesis.py +420 -0
  296. attocode_intel/rules/testing.py +326 -0
  297. attocode_intel/server.py +734 -0
  298. attocode_intel/service.py +3416 -0
  299. attocode_intel/sharing.py +57 -0
  300. attocode_intel/storage/__init__.py +6 -0
  301. attocode_intel/storage/branch_overlay.py +352 -0
  302. attocode_intel/storage/content_store.py +220 -0
  303. attocode_intel/storage/dependency_store.py +143 -0
  304. attocode_intel/storage/diff_engine.py +187 -0
  305. attocode_intel/storage/embedding_store.py +594 -0
  306. attocode_intel/storage/provenance_store.py +137 -0
  307. attocode_intel/storage/security_scanner_db.py +164 -0
  308. attocode_intel/storage/symbol_store.py +160 -0
  309. attocode_intel/structural_evidence.py +144 -0
  310. attocode_intel/symbol_inspection.py +141 -0
  311. attocode_intel/symbol_links.py +167 -0
  312. attocode_intel/telemetry.py +42 -0
  313. attocode_intel/test_ranking.py +73 -0
  314. attocode_intel/testing/__init__.py +43 -0
  315. attocode_intel/testing/fixtures.py +122 -0
  316. attocode_intel/testing/helpers.py +75 -0
  317. attocode_intel/testing/mocks.py +99 -0
  318. attocode_intel/tools/__init__.py +2 -0
  319. attocode_intel/tools/adr_tools.py +628 -0
  320. attocode_intel/tools/analysis_tools.py +382 -0
  321. attocode_intel/tools/composite_tools.py +695 -0
  322. attocode_intel/tools/cross_mode_tools.py +173 -0
  323. attocode_intel/tools/dead_code_tools.py +582 -0
  324. attocode_intel/tools/distill_tools.py +353 -0
  325. attocode_intel/tools/frecency_tools.py +220 -0
  326. attocode_intel/tools/fuzzy_tools.py +255 -0
  327. attocode_intel/tools/history_tools.py +486 -0
  328. attocode_intel/tools/learning_tools.py +222 -0
  329. attocode_intel/tools/lsp_tools.py +218 -0
  330. attocode_intel/tools/maintenance_tools.py +1662 -0
  331. attocode_intel/tools/navigation_tools.py +220 -0
  332. attocode_intel/tools/overlay_tools.py +540 -0
  333. attocode_intel/tools/pin_store.py +432 -0
  334. attocode_intel/tools/pin_tools.py +266 -0
  335. attocode_intel/tools/query_constraints_tools.py +262 -0
  336. attocode_intel/tools/query_history_tools.py +158 -0
  337. attocode_intel/tools/readiness_tools.py +44 -0
  338. attocode_intel/tools/rule_tools.py +992 -0
  339. attocode_intel/tools/search_tools.py +770 -0
  340. attocode_intel/tools/snapshot_tools.py +624 -0
  341. attocode_intel/workers/__init__.py +1 -0
  342. attocode_intel/workers/job_utils.py +111 -0
  343. attocode_intel/workers/jobs.py +573 -0
  344. attocode_intel/workers/run.py +30 -0
  345. attocode_intel/workers/settings.py +61 -0
@@ -0,0 +1,417 @@
1
+ """Query history tracker with combo boosting.
2
+
3
+ Tracks successful query-result pairs to boost files that are repeatedly
4
+ opened with the same query. This is different from frecency which tracks
5
+ file access patterns.
6
+
7
+ Combo boosting:
8
+ - When user searches "foo" and opens "bar.py", we track that mapping
9
+ - If user repeatedly opens "bar.py" when searching "foo", we boost "bar.py"
10
+ - This helps prioritize commonly co-occuring results
11
+
12
+ Based on fff.nvim's query_tracker.rs.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import sqlite3
19
+ import threading
20
+ import time
21
+ from dataclasses import dataclass
22
+ from pathlib import Path
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # Combo boosting constants
27
+ _MIN_COMBO_COUNT: int = 3 # Minimum selections before combo boost applies
28
+ _COMBO_BOOST_MULTIPLIER: float = 100.0 # Score multiplier for combo matches
29
+ _MAX_HISTORY_DAYS: int = 30 # Only consider selections within 30 days
30
+
31
+ # SQLite schema version
32
+ _SCHEMA_VERSION: int = 1
33
+
34
+
35
+ @dataclass
36
+ class QueryResult:
37
+ """A tracked query result."""
38
+ query: str
39
+ file_path: str
40
+ count: int # How many times this query led to opening this file
41
+ last_selected: float # Unix timestamp of last selection
42
+ combo_score: float # Calculated combo boost score
43
+
44
+
45
+ @dataclass
46
+ class QueryHistoryStats:
47
+ """Statistics about the query history."""
48
+ total_queries: int
49
+ total_selections: int
50
+ unique_queries: int
51
+ unique_files: int
52
+ combo_boosts: int # Number of files with combo boost > 0
53
+
54
+
55
+ class QueryHistoryTracker:
56
+ """Tracks query-result pairs for combo boosting.
57
+
58
+ Stores which files were selected after which queries, allowing
59
+ us to boost results that commonly appear together.
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ db_path: str | Path = ".attocode/query_history",
65
+ *,
66
+ min_combo_count: int = _MIN_COMBO_COUNT,
67
+ combo_boost_multiplier: float = _COMBO_BOOST_MULTIPLIER,
68
+ ) -> None:
69
+ self._db_path = Path(db_path)
70
+ self._min_combo_count = min_combo_count
71
+ self._combo_boost_multiplier = combo_boost_multiplier
72
+ self._lock = threading.Lock()
73
+ self._conn: sqlite3.Connection | None = None
74
+ self._ensure_db()
75
+
76
+ def _ensure_db(self) -> None:
77
+ """Ensure the database directory and tables exist."""
78
+ self._db_path.mkdir(parents=True, exist_ok=True)
79
+ db_file = self._db_path / "query_history.db"
80
+
81
+ conn = sqlite3.connect(
82
+ str(db_file),
83
+ timeout=5.0,
84
+ check_same_thread=False,
85
+ )
86
+ conn.execute("PRAGMA journal_mode=WAL")
87
+ conn.execute("PRAGMA foreign_keys=ON")
88
+
89
+ # Main table: query_selections
90
+ conn.execute("""
91
+ CREATE TABLE IF NOT EXISTS query_selections (
92
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
93
+ query TEXT NOT NULL,
94
+ file_path TEXT NOT NULL,
95
+ count INTEGER NOT NULL DEFAULT 1,
96
+ last_selected REAL NOT NULL,
97
+ UNIQUE(query, file_path)
98
+ )
99
+ """)
100
+
101
+ # Index for fast lookups
102
+ conn.execute("""
103
+ CREATE INDEX IF NOT EXISTS idx_query
104
+ ON query_selections(query)
105
+ """)
106
+
107
+ conn.execute("""
108
+ CREATE INDEX IF NOT EXISTS idx_file
109
+ ON query_selections(file_path)
110
+ """)
111
+
112
+ # Meta table for schema version
113
+ conn.execute("""
114
+ CREATE TABLE IF NOT EXISTS meta (
115
+ key TEXT PRIMARY KEY,
116
+ value TEXT NOT NULL
117
+ )
118
+ """)
119
+
120
+ cursor = conn.execute(
121
+ "SELECT value FROM meta WHERE key = 'version'",
122
+ )
123
+ row = cursor.fetchone()
124
+ if row is None:
125
+ conn.execute(
126
+ "INSERT INTO meta (key, value) VALUES ('version', ?)",
127
+ (str(_SCHEMA_VERSION),),
128
+ )
129
+ conn.commit()
130
+
131
+ self._conn = conn
132
+
133
+ def close(self) -> None:
134
+ """Close the database connection."""
135
+ with self._lock:
136
+ if self._conn is not None:
137
+ self._conn.close()
138
+ self._conn = None
139
+
140
+ def __del__(self) -> None:
141
+ try:
142
+ self.close()
143
+ except Exception:
144
+ pass
145
+
146
+ def track_selection(self, query: str, file_path: str) -> None:
147
+ """Record that a user selected a file after searching for query.
148
+
149
+ Args:
150
+ query: The search query.
151
+ file_path: The file that was selected/opened.
152
+ """
153
+ now = time.time()
154
+ query_lower = query.lower().strip()
155
+ file_path = str(file_path)
156
+
157
+ with self._lock:
158
+ if self._conn is None:
159
+ return
160
+
161
+ # Try to update existing
162
+ cursor = self._conn.execute(
163
+ """
164
+ UPDATE query_selections
165
+ SET count = count + 1, last_selected = ?
166
+ WHERE query = ? AND file_path = ?
167
+ """,
168
+ (now, query_lower, file_path),
169
+ )
170
+
171
+ if cursor.rowcount == 0:
172
+ # Insert new record
173
+ self._conn.execute(
174
+ """
175
+ INSERT INTO query_selections (query, file_path, count, last_selected)
176
+ VALUES (?, ?, 1, ?)
177
+ """,
178
+ (query_lower, file_path, now),
179
+ )
180
+
181
+ self._conn.commit()
182
+
183
+ # Prune old entries
184
+ cutoff = now - (_MAX_HISTORY_DAYS * 86400)
185
+ self._conn.execute(
186
+ "DELETE FROM query_selections WHERE last_selected < ?",
187
+ (cutoff,),
188
+ )
189
+ self._conn.commit()
190
+
191
+ def get_combo_boost(self, query: str, file_path: str) -> float:
192
+ """Get the combo boost score for a query+file pair.
193
+
194
+ Args:
195
+ query: The search query.
196
+ file_path: The file to get the boost for.
197
+
198
+ Returns:
199
+ Combo boost score (0 if below min_combo_count).
200
+ """
201
+ query_lower = query.lower().strip()
202
+ file_path = str(file_path)
203
+
204
+ with self._lock:
205
+ if self._conn is None:
206
+ return 0.0
207
+
208
+ cursor = self._conn.execute(
209
+ """
210
+ SELECT count, last_selected FROM query_selections
211
+ WHERE query = ? AND file_path = ?
212
+ """,
213
+ (query_lower, file_path),
214
+ )
215
+ row = cursor.fetchone()
216
+
217
+ if row is None:
218
+ return 0.0
219
+
220
+ count, last_selected = row
221
+
222
+ # Check if within history window
223
+ now = time.time()
224
+ cutoff = now - (_MAX_HISTORY_DAYS * 86400)
225
+ if last_selected < cutoff:
226
+ return 0.0
227
+
228
+ # Apply combo boost if above threshold
229
+ if count >= self._min_combo_count:
230
+ return float(count) * self._combo_boost_multiplier
231
+
232
+ return 0.0
233
+
234
+ def get_combo_boosts_batch(
235
+ self,
236
+ query: str,
237
+ file_paths: list[str],
238
+ ) -> dict[str, float]:
239
+ """Get combo boost scores for multiple files for a query.
240
+
241
+ Args:
242
+ query: The search query.
243
+ file_paths: List of file paths to check.
244
+
245
+ Returns:
246
+ Dict mapping file_path -> combo_boost_score.
247
+ """
248
+ boosts: dict[str, float] = {}
249
+ for path in file_paths:
250
+ boosts[path] = self.get_combo_boost(query, path)
251
+ return boosts
252
+
253
+ def get_top_files_for_query(
254
+ self,
255
+ query: str,
256
+ limit: int = 10,
257
+ ) -> list[tuple[str, int, float]]:
258
+ """Get the top files for a given query.
259
+
260
+ Args:
261
+ query: The search query.
262
+ limit: Maximum number of results.
263
+
264
+ Returns:
265
+ List of (file_path, count, combo_score) tuples.
266
+ """
267
+ query_lower = query.lower().strip()
268
+
269
+ with self._lock:
270
+ if self._conn is None:
271
+ return []
272
+
273
+ cursor = self._conn.execute(
274
+ """
275
+ SELECT file_path, count, last_selected
276
+ FROM query_selections
277
+ WHERE query = ? AND last_selected > ?
278
+ ORDER BY count DESC
279
+ LIMIT ?
280
+ """,
281
+ (query_lower, time.time() - (_MAX_HISTORY_DAYS * 86400), limit),
282
+ )
283
+
284
+ results: list[tuple[str, int, float]] = []
285
+ for row in cursor.fetchall():
286
+ file_path, count, last_selected = row
287
+ combo_score = (
288
+ float(count) * self._combo_boost_multiplier
289
+ if count >= self._min_combo_count
290
+ else 0.0
291
+ )
292
+ results.append((file_path, count, combo_score))
293
+
294
+ return results
295
+
296
+ def get_stats(self) -> QueryHistoryStats:
297
+ """Get statistics about the query history."""
298
+ with self._lock:
299
+ if self._conn is None:
300
+ return QueryHistoryStats(
301
+ total_queries=0,
302
+ total_selections=0,
303
+ unique_queries=0,
304
+ unique_files=0,
305
+ combo_boosts=0,
306
+ )
307
+
308
+ # Total selections
309
+ cursor = self._conn.execute(
310
+ "SELECT COALESCE(SUM(count), 0) FROM query_selections",
311
+ )
312
+ total_selections = cursor.fetchone()[0] or 0
313
+
314
+ # Total queries
315
+ cursor = self._conn.execute(
316
+ "SELECT COUNT(*) FROM query_selections",
317
+ )
318
+ total_queries = cursor.fetchone()[0] or 0
319
+
320
+ # Unique queries
321
+ cursor = self._conn.execute(
322
+ "SELECT COUNT(DISTINCT query) FROM query_selections",
323
+ )
324
+ unique_queries = cursor.fetchone()[0] or 0
325
+
326
+ # Unique files
327
+ cursor = self._conn.execute(
328
+ "SELECT COUNT(DISTINCT file_path) FROM query_selections",
329
+ )
330
+ unique_files = cursor.fetchone()[0] or 0
331
+
332
+ # Files with combo boost > 0
333
+ cursor = self._conn.execute(
334
+ """
335
+ SELECT COUNT(*) FROM query_selections
336
+ WHERE count >= ?
337
+ """,
338
+ (self._min_combo_count,),
339
+ )
340
+ combo_boosts = cursor.fetchone()[0] or 0
341
+
342
+ return QueryHistoryStats(
343
+ total_queries=total_queries,
344
+ total_selections=total_selections,
345
+ unique_queries=unique_queries,
346
+ unique_files=unique_files,
347
+ combo_boosts=combo_boosts,
348
+ )
349
+
350
+ def clear(self, query: str | None = None, file_path: str | None = None) -> int:
351
+ """Clear query history.
352
+
353
+ Args:
354
+ query: If provided, clear only this query. If None, clear all.
355
+ file_path: If provided with query, clear only that pair.
356
+
357
+ Returns:
358
+ Number of entries cleared.
359
+ """
360
+ with self._lock:
361
+ if self._conn is None:
362
+ return 0
363
+
364
+ if query is None:
365
+ self._conn.execute("DELETE FROM query_selections")
366
+ self._conn.commit()
367
+ return -1
368
+
369
+ query_lower = query.lower().strip()
370
+
371
+ if file_path is None:
372
+ cursor = self._conn.execute(
373
+ "DELETE FROM query_selections WHERE query = ?",
374
+ (query_lower,),
375
+ )
376
+ else:
377
+ cursor = self._conn.execute(
378
+ "DELETE FROM query_selections WHERE query = ? AND file_path = ?",
379
+ (query_lower, str(file_path)),
380
+ )
381
+
382
+ self._conn.commit()
383
+ return cursor.rowcount
384
+
385
+
386
+ # Global singleton instance
387
+ _tracker: QueryHistoryTracker | None = None
388
+ _tracker_lock = threading.Lock()
389
+
390
+
391
+ def get_query_tracker(
392
+ db_path: str | Path = ".attocode/query_history",
393
+ *,
394
+ min_combo_count: int = _MIN_COMBO_COUNT,
395
+ combo_boost_multiplier: float = _COMBO_BOOST_MULTIPLIER,
396
+ ) -> QueryHistoryTracker:
397
+ """Get or create the global QueryHistoryTracker instance."""
398
+ global _tracker
399
+
400
+ with _tracker_lock:
401
+ if _tracker is None:
402
+ _tracker = QueryHistoryTracker(
403
+ db_path=db_path,
404
+ min_combo_count=min_combo_count,
405
+ combo_boost_multiplier=combo_boost_multiplier,
406
+ )
407
+ return _tracker
408
+
409
+
410
+ def reset_query_tracker() -> None:
411
+ """Reset the global tracker instance."""
412
+ global _tracker
413
+
414
+ with _tracker_lock:
415
+ if _tracker is not None:
416
+ _tracker.close()
417
+ _tracker = None