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,159 @@
1
+ """Retrieval pins — make ranked-result tools deterministic across calls.
2
+
3
+ A ``RetrievalPin`` is a content-addressed snapshot of the state of every
4
+ code-intel store at a moment in time. Every ranked-result tool
5
+ (``semantic_search``, ``fast_search``, ``repo_map``, etc.) emits one in its
6
+ response; a caller can then pass the pin back to ``retrieve_with_pin`` to
7
+ re-run the same query against the same state, with a loud drift error if
8
+ the underlying stores have changed.
9
+
10
+ The per-store hash is cheap: ``sha256(max(updated_at) || row_count || schema_version)``.
11
+ That's stable under no-op reads and changes on any write.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import base64
17
+ import hashlib
18
+ import json
19
+ import secrets
20
+ import time
21
+ from dataclasses import asdict, dataclass, field
22
+ from typing import TYPE_CHECKING, Any
23
+
24
+ from attocode_intel.artifacts.hashing import canonical_json
25
+
26
+ if TYPE_CHECKING:
27
+ from collections.abc import Mapping
28
+
29
+ PIN_SCHEMA_VERSION = 1
30
+ DEFAULT_TTL_SECONDS = 86_400 # 24 hours
31
+
32
+
33
+ def compute_store_hash(
34
+ *,
35
+ schema_version: str | int,
36
+ row_count: int,
37
+ max_updated_at: float | None,
38
+ extra: Mapping[str, Any] | None = None,
39
+ ) -> str:
40
+ """Compute a cheap, stable content hash for one store's current state.
41
+
42
+ Intentionally only uses three fields (schema version, row count, latest
43
+ ``updated_at``) so the hash can be computed with a single cheap SQL
44
+ aggregate query. ``extra`` is for stores that need to mix in something
45
+ else (e.g. active embedding model pointer) — pass a small dict, it goes
46
+ through canonical JSON.
47
+
48
+ Two stores on two machines with exactly the same rows should produce
49
+ the same hash; mutations (insert/update/delete) change it; pure reads
50
+ do not.
51
+ """
52
+ body = {
53
+ "schema": str(schema_version),
54
+ "rows": int(row_count),
55
+ "max_updated_at": float(max_updated_at or 0.0),
56
+ "extra": dict(extra or {}),
57
+ }
58
+ return hashlib.sha256(canonical_json(body)).hexdigest()
59
+
60
+
61
+ def make_pin_id() -> str:
62
+ """Return a short, URL-safe, unguessable pin id: ``pin_<22 base32 chars>``."""
63
+ raw = secrets.token_bytes(16)
64
+ token = base64.b32encode(raw).decode("ascii").rstrip("=").lower()
65
+ return f"pin_{token}"
66
+
67
+
68
+ @dataclass(frozen=True, slots=True)
69
+ class RetrievalPin:
70
+ """Immutable snapshot of code-intel state.
71
+
72
+ Fields:
73
+ - ``pin_id``: short identifier (see :func:`make_pin_id`).
74
+ - ``schema_version``: for forward-compat.
75
+ - ``manifest_hashes``: per-store name → hex store hash.
76
+ - ``manifest_hash``: sha256 of the canonical JSON of ``manifest_hashes``
77
+ (plus overlay_id). This is the thing to compare when checking drift.
78
+ - ``overlay_id``: optional branch/overlay identifier (local side).
79
+ - ``branch_id``: optional DB branch id (server side).
80
+ - ``created_at``, ``expires_at``: unix seconds; 0 for no expiry.
81
+ """
82
+
83
+ pin_id: str
84
+ schema_version: int
85
+ manifest_hashes: dict[str, str] = field(default_factory=dict)
86
+ manifest_hash: str = ""
87
+ overlay_id: str | None = None
88
+ branch_id: str | None = None
89
+ created_at: float = 0.0
90
+ expires_at: float = 0.0
91
+
92
+ @classmethod
93
+ def create(
94
+ cls,
95
+ *,
96
+ manifest_hashes: Mapping[str, str],
97
+ overlay_id: str | None = None,
98
+ branch_id: str | None = None,
99
+ ttl_seconds: int = DEFAULT_TTL_SECONDS,
100
+ pin_id: str | None = None,
101
+ ) -> RetrievalPin:
102
+ """Build a new pin from a dict of per-store hashes."""
103
+ manifest = {str(k): str(v) for k, v in sorted(manifest_hashes.items())}
104
+ manifest_hash = hashlib.sha256(
105
+ canonical_json({"manifest": manifest, "overlay": overlay_id, "branch": branch_id})
106
+ ).hexdigest()
107
+ now = time.time()
108
+ return cls(
109
+ pin_id=pin_id or make_pin_id(),
110
+ schema_version=PIN_SCHEMA_VERSION,
111
+ manifest_hashes=dict(manifest),
112
+ manifest_hash=manifest_hash,
113
+ overlay_id=overlay_id,
114
+ branch_id=branch_id,
115
+ created_at=now,
116
+ expires_at=now + ttl_seconds if ttl_seconds > 0 else 0.0,
117
+ )
118
+
119
+ def is_expired(self, now: float | None = None) -> bool:
120
+ """Whether the pin has passed its TTL. Pins with expires_at == 0 never expire."""
121
+ if self.expires_at <= 0:
122
+ return False
123
+ return (now if now is not None else time.time()) >= self.expires_at
124
+
125
+ def drift_from(self, current_hashes: Mapping[str, str]) -> dict[str, tuple[str, str]]:
126
+ """Return a map of store_name -> (pinned_hash, current_hash) for any store that
127
+ changed vs the pinned state. Empty dict means no drift.
128
+
129
+ Stores present in one side but not the other are reported with the
130
+ missing side as ``""`` — this is usually a schema change and should
131
+ be treated as drift.
132
+ """
133
+ drift: dict[str, tuple[str, str]] = {}
134
+ all_keys = set(self.manifest_hashes) | set(current_hashes)
135
+ for key in all_keys:
136
+ pinned = self.manifest_hashes.get(key, "")
137
+ current = current_hashes.get(key, "")
138
+ if pinned != current:
139
+ drift[key] = (pinned, current)
140
+ return drift
141
+
142
+ def to_dict(self) -> dict[str, Any]:
143
+ return asdict(self)
144
+
145
+ def to_json(self) -> str:
146
+ return json.dumps(self.to_dict(), sort_keys=True, ensure_ascii=True)
147
+
148
+ @classmethod
149
+ def from_dict(cls, data: Mapping[str, Any]) -> RetrievalPin:
150
+ return cls(
151
+ pin_id=str(data["pin_id"]),
152
+ schema_version=int(data.get("schema_version", PIN_SCHEMA_VERSION)),
153
+ manifest_hashes=dict(data.get("manifest_hashes", {})),
154
+ manifest_hash=str(data.get("manifest_hash", "")),
155
+ overlay_id=data.get("overlay_id"),
156
+ branch_id=data.get("branch_id"),
157
+ created_at=float(data.get("created_at", 0.0)),
158
+ expires_at=float(data.get("expires_at", 0.0)),
159
+ )
@@ -0,0 +1,59 @@
1
+ """Audit event logging utility — writes to DB and publishes via Redis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import uuid
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from sqlalchemy.ext.asyncio import AsyncSession
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ async def log_event(
16
+ session: AsyncSession,
17
+ org_id: uuid.UUID,
18
+ event_type: str,
19
+ *,
20
+ repo_id: uuid.UUID | None = None,
21
+ user_id: uuid.UUID | None = None,
22
+ detail: dict | None = None,
23
+ ) -> None:
24
+ """Log an audit event to DB and publish to Redis for real-time streaming.
25
+
26
+ Args:
27
+ session: Active DB session (caller should commit).
28
+ org_id: Organization ID.
29
+ event_type: Event type string (e.g. "org.created", "api_key.revoked").
30
+ repo_id: Optional repository ID.
31
+ user_id: Optional user ID who performed the action.
32
+ detail: Optional JSON-serializable detail dict.
33
+ """
34
+ from attocode_intel.db.models import AuditEvent
35
+
36
+ event = AuditEvent(
37
+ org_id=org_id,
38
+ repo_id=repo_id,
39
+ user_id=user_id,
40
+ event_type=event_type,
41
+ detail=detail or {},
42
+ )
43
+ session.add(event)
44
+
45
+ # Also publish to Redis for real-time WebSocket streaming
46
+ try:
47
+ from attocode_intel.pubsub import publish_event
48
+
49
+ channel_id = str(repo_id) if repo_id else str(org_id)
50
+ await publish_event(channel_id, f"audit.{event_type}", {
51
+ "org_id": str(org_id),
52
+ "repo_id": str(repo_id) if repo_id else None,
53
+ "user_id": str(user_id) if user_id else None,
54
+ "event_type": event_type,
55
+ "detail": detail or {},
56
+ })
57
+ except Exception:
58
+ # Don't fail the main operation if Redis publish fails
59
+ logger.debug("Failed to publish audit event to Redis", exc_info=True)
@@ -0,0 +1,206 @@
1
+ """Bug finder — scans diffs for potential issues.
2
+
3
+ Analyzes code changes between branches, looking for common
4
+ bug patterns, edge cases, security issues, and performance
5
+ problems. Reports findings with confidence levels.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ import re
12
+ from dataclasses import dataclass, field
13
+ from enum import StrEnum
14
+
15
+ from attocode_intel._internal.types.severity import Severity
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class FindingCategory(StrEnum):
21
+ """Category of bug finding."""
22
+ LOGIC_ERROR = "logic_error"
23
+ EDGE_CASE = "edge_case"
24
+ SECURITY = "security"
25
+ PERFORMANCE = "performance"
26
+ ERROR_HANDLING = "error_handling"
27
+ TYPE_SAFETY = "type_safety"
28
+ CONCURRENCY = "concurrency"
29
+ RESOURCE_LEAK = "resource_leak"
30
+
31
+
32
+ @dataclass(slots=True)
33
+ class Finding:
34
+ """A single bug finding."""
35
+ file: str
36
+ line: int
37
+ severity: Severity
38
+ category: FindingCategory
39
+ confidence: float # 0.0-1.0
40
+ description: str
41
+ suggestion: str = ""
42
+ code_snippet: str = ""
43
+
44
+
45
+ @dataclass(slots=True)
46
+ class BugReport:
47
+ """Complete bug report for a scan."""
48
+ findings: list[Finding] = field(default_factory=list)
49
+ files_scanned: int = 0
50
+ lines_analyzed: int = 0
51
+ scan_duration: float = 0.0
52
+
53
+ @property
54
+ def critical_count(self) -> int:
55
+ return sum(1 for f in self.findings if f.severity == Severity.CRITICAL)
56
+
57
+ @property
58
+ def high_count(self) -> int:
59
+ return sum(1 for f in self.findings if f.severity == Severity.HIGH)
60
+
61
+ def filter_by_confidence(self, min_confidence: float = 0.5) -> list[Finding]:
62
+ """Filter findings by minimum confidence level."""
63
+ return [f for f in self.findings if f.confidence >= min_confidence]
64
+
65
+ def format_report(self, *, min_confidence: float = 0.5) -> str:
66
+ """Format findings as a human-readable report."""
67
+ filtered = self.filter_by_confidence(min_confidence)
68
+ if not filtered:
69
+ return f"No findings above {min_confidence:.0%} confidence ({self.files_scanned} files scanned)."
70
+
71
+ lines = [
72
+ f"Bug Report: {len(filtered)} findings ({self.files_scanned} files, {self.lines_analyzed} lines)",
73
+ "",
74
+ ]
75
+
76
+ # Group by severity
77
+ for severity in (Severity.CRITICAL, Severity.HIGH, Severity.MEDIUM, Severity.LOW):
78
+ group = [f for f in filtered if f.severity == severity]
79
+ if not group:
80
+ continue
81
+ lines.append(f"### {severity.value.upper()} ({len(group)})")
82
+ for finding in group:
83
+ confidence_pct = f"{finding.confidence:.0%}"
84
+ lines.append(
85
+ f" [{confidence_pct}] {finding.file}:{finding.line} — "
86
+ f"{finding.description}"
87
+ )
88
+ if finding.suggestion:
89
+ lines.append(f" Fix: {finding.suggestion}")
90
+ lines.append("")
91
+
92
+ return "\n".join(lines)
93
+
94
+
95
+ # --- Static pattern-based analysis ---
96
+
97
+ _PATTERNS: list[tuple[str, FindingCategory, Severity, str, float]] = [
98
+ # (regex, category, severity, description_template, confidence)
99
+ (r"except\s*:", FindingCategory.ERROR_HANDLING, Severity.MEDIUM,
100
+ "Bare except clause catches all exceptions including KeyboardInterrupt", 0.9),
101
+ (r"eval\s*\(", FindingCategory.SECURITY, Severity.HIGH,
102
+ "Use of eval() can execute arbitrary code", 0.85),
103
+ (r"exec\s*\(", FindingCategory.SECURITY, Severity.HIGH,
104
+ "Use of exec() can execute arbitrary code", 0.8),
105
+ (r"subprocess\.call\(.*shell\s*=\s*True", FindingCategory.SECURITY, Severity.HIGH,
106
+ "subprocess with shell=True is vulnerable to injection", 0.85),
107
+ (r"\.format\(.*\)\s*$", FindingCategory.SECURITY, Severity.LOW,
108
+ "Consider f-strings over .format() for clarity", 0.3),
109
+ (r"TODO|FIXME|HACK|XXX", FindingCategory.LOGIC_ERROR, Severity.INFO,
110
+ "Unresolved TODO/FIXME marker", 0.95),
111
+ (r"time\.sleep\(\d{2,}", FindingCategory.PERFORMANCE, Severity.MEDIUM,
112
+ "Long sleep duration may indicate polling pattern", 0.6),
113
+ (r"except\s+Exception\s+as\s+\w+:\s*\n\s*pass", FindingCategory.ERROR_HANDLING, Severity.MEDIUM,
114
+ "Exception silently swallowed", 0.75),
115
+ ]
116
+
117
+
118
+ def scan_text(
119
+ text: str,
120
+ file_path: str = "<unknown>",
121
+ *,
122
+ line_offset: int = 0,
123
+ ) -> list[Finding]:
124
+ """Scan text for common bug patterns.
125
+
126
+ Args:
127
+ text: Source code text.
128
+ file_path: File path for reporting.
129
+ line_offset: Line offset for diff-based scanning.
130
+
131
+ Returns:
132
+ List of findings.
133
+ """
134
+ findings: list[Finding] = []
135
+ lines = text.split("\n")
136
+
137
+ for i, line in enumerate(lines, start=1 + line_offset):
138
+ for pattern, category, severity, desc, confidence in _PATTERNS:
139
+ if re.search(pattern, line):
140
+ findings.append(Finding(
141
+ file=file_path,
142
+ line=i,
143
+ severity=severity,
144
+ category=category,
145
+ confidence=confidence,
146
+ description=desc,
147
+ code_snippet=line.strip()[:120],
148
+ ))
149
+
150
+ return findings
151
+
152
+
153
+ _HUNK_RE = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)")
154
+
155
+
156
+ def _added_lines(diff_text: str) -> list[tuple[str, list[tuple[int, str]]]]:
157
+ """Added lines per file, numbered as they sit in the new file.
158
+
159
+ A hunk header sets the new-file line; context lines advance it, removed
160
+ lines do not. A diff without hunk headers numbers added lines from 1.
161
+ """
162
+ files: list[tuple[str, list[tuple[int, str]]]] = []
163
+ lineno = 1
164
+ for line in diff_text.split("\n"):
165
+ if line.startswith("+++ b/"):
166
+ files.append((line[6:], []))
167
+ lineno = 1
168
+ elif not files:
169
+ continue
170
+ elif m := _HUNK_RE.match(line):
171
+ lineno = int(m.group(1))
172
+ elif line.startswith("+") and not line.startswith("+++"):
173
+ files[-1][1].append((lineno, line[1:]))
174
+ lineno += 1
175
+ elif line.startswith(" ") or not line: # context; some tools strip the space
176
+ lineno += 1
177
+ return [(path, added) for path, added in files if added]
178
+
179
+
180
+ def parse_diff_files(diff_text: str) -> list[tuple[str, str]]:
181
+ """Parse a unified diff into (file_path, changed_content) pairs."""
182
+ return [(path, "\n".join(text for _, text in added)) for path, added in _added_lines(diff_text)]
183
+
184
+
185
+ def scan_diff(diff_text: str) -> BugReport:
186
+ """Scan a unified diff for bug patterns.
187
+
188
+ Parses the diff, extracts added lines, and runs
189
+ pattern-based analysis on each changed file.
190
+ """
191
+ files = _added_lines(diff_text)
192
+ all_findings: list[Finding] = []
193
+ total_lines = 0
194
+
195
+ for file_path, added in files:
196
+ if not file_path.endswith((".py", ".js", ".ts", ".go", ".rs", ".java")):
197
+ continue
198
+ for lineno, text in added:
199
+ all_findings.extend(scan_text(text, file_path, line_offset=lineno - 1))
200
+ total_lines += len(added)
201
+
202
+ return BugReport(
203
+ findings=all_findings,
204
+ files_scanned=len(files),
205
+ lines_analyzed=total_lines,
206
+ )
@@ -0,0 +1,80 @@
1
+ """Portable local artifact bundles for code-intel state."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import tarfile
7
+ from datetime import UTC, datetime
8
+ from pathlib import Path
9
+ from tempfile import TemporaryDirectory
10
+
11
+ from attocode_intel import __version__
12
+ from attocode_intel.artifacts.hashing import sha256_file
13
+
14
+ _SCHEMA_VERSION = 1
15
+ _ARTIFACTS = (
16
+ ("artifacts/index/symbols.db", ".attocode/index/symbols.db"),
17
+ ("artifacts/vectors/embeddings.db", ".attocode/vectors/embeddings.db"),
18
+ ("artifacts/cache/memory.db", ".attocode/cache/memory.db"),
19
+ ("artifacts/adrs.db", ".attocode/adrs.db"),
20
+ )
21
+
22
+
23
+ def _metadata(project_dir: Path) -> dict[str, object]:
24
+ artifacts: list[dict[str, object]] = []
25
+ for bundle_path, rel_path in _ARTIFACTS:
26
+ source = project_dir / rel_path
27
+ present = source.exists()
28
+ artifacts.append({
29
+ "path": bundle_path,
30
+ "present": present,
31
+ "size_bytes": source.stat().st_size if present else 0,
32
+ "sha256": sha256_file(source) if present else None,
33
+ })
34
+
35
+ return {
36
+ "schema_version": _SCHEMA_VERSION,
37
+ "created_at": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
38
+ "project_name": project_dir.name,
39
+ "project_root_basename": project_dir.name,
40
+ "attocode_version": __version__,
41
+ "artifacts": artifacts,
42
+ }
43
+
44
+
45
+ def export_bundle(project_dir: str, output_path: str) -> Path:
46
+ """Export local code-intel artifacts into a tar.gz bundle."""
47
+ project_root = Path(project_dir).resolve()
48
+ destination = Path(output_path).resolve()
49
+ destination.parent.mkdir(parents=True, exist_ok=True)
50
+ metadata = _metadata(project_root)
51
+
52
+ with TemporaryDirectory(prefix="attocode-bundle-") as tmpdir:
53
+ root = Path(tmpdir) / "attocode-bundle"
54
+ root.mkdir(parents=True, exist_ok=True)
55
+ metadata_path = root / "metadata.json"
56
+ metadata_path.write_text(json.dumps(metadata, indent=2) + "\n", encoding="utf-8")
57
+
58
+ for bundle_path, rel_path in _ARTIFACTS:
59
+ source = project_root / rel_path
60
+ if not source.exists():
61
+ continue
62
+ target = root / bundle_path
63
+ target.parent.mkdir(parents=True, exist_ok=True)
64
+ target.write_bytes(source.read_bytes())
65
+
66
+ with tarfile.open(destination, "w:gz") as archive:
67
+ archive.add(root, arcname="attocode-bundle")
68
+
69
+ return destination
70
+
71
+
72
+ def inspect_bundle(bundle_path: str) -> dict[str, object]:
73
+ """Read bundle metadata without touching local project state."""
74
+ bundle = Path(bundle_path).resolve()
75
+ with tarfile.open(bundle, "r:gz") as archive:
76
+ metadata_member = archive.getmember("attocode-bundle/metadata.json")
77
+ fh = archive.extractfile(metadata_member)
78
+ if fh is None:
79
+ raise FileNotFoundError("metadata.json missing from bundle")
80
+ return json.loads(fh.read().decode("utf-8"))