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,351 @@
1
+ """Dependency injection for the HTTP API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import uuid
7
+ from typing import TYPE_CHECKING, Annotated
8
+
9
+ from fastapi import HTTPException, Query
10
+
11
+ from attocode_intel.config import CodeIntelConfig
12
+ from attocode_intel.service import CodeIntelService
13
+
14
+ if TYPE_CHECKING:
15
+ from collections.abc import AsyncGenerator
16
+
17
+ from sqlalchemy.ext.asyncio import AsyncSession
18
+
19
+ from attocode_intel.api.auth.context import AuthContext
20
+ from attocode_intel.git.manager import GitRepoManager
21
+
22
+ # N2: Shared BranchParam type alias — used by all route modules
23
+ BranchParam = Annotated[str, Query(alias="branch", description="Branch name (empty = default/working dir)")]
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # Module-level singletons set during app startup
28
+ _config: CodeIntelConfig | None = None
29
+ _services: dict[str, CodeIntelService] = {}
30
+ _default_project_id: str = ""
31
+
32
+
33
+ def configure(config: CodeIntelConfig) -> None:
34
+ """Initialize the DI container with a config. Called once at app startup."""
35
+ global _config, _default_project_id
36
+ if _config is not None:
37
+ logger.warning("configure() called more than once; overwriting previous config")
38
+ _config = config
39
+ if config.project_dir:
40
+ svc = CodeIntelService.get_instance(config.project_dir, config)
41
+ _default_project_id = "default"
42
+ _services["default"] = svc
43
+
44
+
45
+ def reset() -> None:
46
+ """Reset all state. For test isolation only."""
47
+ global _config, _default_project_id
48
+ _config = None
49
+ _services.clear()
50
+ _default_project_id = ""
51
+
52
+
53
+ def get_config() -> CodeIntelConfig:
54
+ """Return the active config."""
55
+ if _config is None:
56
+ return CodeIntelConfig.from_env()
57
+ return _config
58
+
59
+
60
+ def ensure_branch_supported(branch: str, *, allow_service_mode: bool = False) -> None:
61
+ """Reject unsupported branch scoping.
62
+
63
+ Local/CLI mode uses the working tree directly, so non-empty branch values
64
+ are not supported. Text v1 routes also reject branch overlays unless the
65
+ caller explicitly opts into service-mode handling.
66
+ """
67
+ if not branch:
68
+ return
69
+
70
+ config = get_config()
71
+ if config.is_service_mode and allow_service_mode:
72
+ return
73
+
74
+ detail = (
75
+ "Branch scoping is only supported on branch-aware service-mode endpoints; "
76
+ "this endpoint uses the working tree and does not accept a non-empty branch."
77
+ )
78
+ raise HTTPException(status_code=422, detail=detail)
79
+
80
+
81
+ def get_service(project_id: str = "") -> CodeIntelService:
82
+ """Return the CodeIntelService for a project.
83
+
84
+ For Phase 1, only a single project is supported (the default).
85
+ Multi-project support comes in Phase 2.
86
+ """
87
+ pid = project_id or _default_project_id
88
+ if pid not in _services:
89
+ raise ValueError(f"Project '{pid}' not found. Register it first via POST /api/v1/projects")
90
+ return _services[pid]
91
+
92
+
93
+ async def get_service_or_404(project_id: str) -> CodeIntelService:
94
+ """Return the service for a project or raise HTTP 404.
95
+
96
+ Supports both CLI-mode string IDs ("default") and service-mode UUIDs.
97
+ For UUIDs in service mode, looks up the repo path from the DB.
98
+ """
99
+ # Fast path: already registered
100
+ if project_id in _services:
101
+ return _services[project_id]
102
+ pid = project_id or _default_project_id
103
+ if pid in _services:
104
+ return _services[pid]
105
+
106
+ # Service mode: try UUID lookup from DB
107
+ config = get_config()
108
+ if config.is_service_mode:
109
+ try:
110
+ uuid.UUID(project_id)
111
+ except ValueError:
112
+ pass
113
+ else:
114
+ from attocode_intel.db.engine import get_engine
115
+
116
+ engine = get_engine()
117
+ if engine is not None:
118
+ from sqlalchemy import text
119
+
120
+ async with engine.connect() as conn:
121
+ result = await conn.execute(
122
+ text("SELECT clone_path, local_path FROM repositories WHERE id = :rid"),
123
+ {"rid": project_id},
124
+ )
125
+ row = result.first()
126
+
127
+ if row is not None:
128
+ path = row[0] or row[1]
129
+ if not path:
130
+ raise HTTPException(
131
+ status_code=422,
132
+ detail=f"Repository '{project_id}' has no indexed content yet",
133
+ )
134
+ return register_project(project_id, path)
135
+
136
+ raise HTTPException(status_code=404, detail=f"Project '{project_id}' not found")
137
+
138
+
139
+ def register_project(project_id: str, path: str, name: str = "") -> CodeIntelService:
140
+ """Register a new project and return its service instance."""
141
+ svc = CodeIntelService.get_instance(path, _config)
142
+ _services[project_id] = svc
143
+ return svc
144
+
145
+
146
+ def list_projects() -> dict[str, CodeIntelService]:
147
+ """Return all registered projects."""
148
+ return dict(_services)
149
+
150
+
151
+ def get_default_project_id() -> str:
152
+ """Return the default project ID."""
153
+ return _default_project_id
154
+
155
+
156
+ # --- Provider factories ---
157
+
158
+
159
+ async def get_analysis_provider(project_id: str):
160
+ """Return the appropriate analysis provider based on mode."""
161
+ config = get_config()
162
+ if config.is_service_mode:
163
+ from attocode_intel.api.providers.db_provider import DbAnalysisProvider
164
+
165
+ return DbAnalysisProvider(project_id)
166
+
167
+ from attocode_intel.api.providers.local_provider import LocalAnalysisProvider
168
+
169
+ svc = await get_service_or_404(project_id)
170
+ return LocalAnalysisProvider(svc)
171
+
172
+
173
+ async def get_search_provider(project_id: str):
174
+ """Return the appropriate search provider based on mode."""
175
+ config = get_config()
176
+ if config.is_service_mode:
177
+ from attocode_intel.api.providers.db_provider import DbSearchProvider
178
+
179
+ return DbSearchProvider(project_id)
180
+
181
+ from attocode_intel.api.providers.local_provider import LocalSearchProvider
182
+
183
+ svc = await get_service_or_404(project_id)
184
+ return LocalSearchProvider(svc)
185
+
186
+
187
+ async def get_graph_provider(project_id: str):
188
+ """Return the appropriate graph provider based on mode."""
189
+ config = get_config()
190
+ if config.is_service_mode:
191
+ from attocode_intel.api.providers.db_provider import DbGraphProvider
192
+
193
+ return DbGraphProvider(project_id)
194
+
195
+ from attocode_intel.api.providers.local_provider import LocalGraphProvider
196
+
197
+ svc = await get_service_or_404(project_id)
198
+ return LocalGraphProvider(svc)
199
+
200
+
201
+ async def get_lsp_provider(project_id: str):
202
+ """Return the LSP provider. Uses DbLSPProvider in service mode for remote repos."""
203
+ config = get_config()
204
+ if config.is_service_mode:
205
+ from attocode_intel.api.providers.db_provider import DbLSPProvider
206
+
207
+ return DbLSPProvider(project_id)
208
+
209
+ from attocode_intel.api.providers.local_provider import LocalLSPProvider
210
+
211
+ svc = await get_service_or_404(project_id)
212
+ return LocalLSPProvider(svc)
213
+
214
+
215
+ # --- Branch context ---
216
+
217
+
218
+ class BranchContext:
219
+ """Resolved branch context for service mode queries.
220
+
221
+ Contains the branch ID and resolved manifest (path → content_sha).
222
+ Passed to service methods so all queries are branch-aware.
223
+ """
224
+
225
+ def __init__(
226
+ self,
227
+ branch_id: uuid.UUID,
228
+ branch_name: str,
229
+ manifest: dict[str, str],
230
+ version: int = 0,
231
+ ) -> None:
232
+ self.branch_id = branch_id
233
+ self.branch_name = branch_name
234
+ self.manifest = manifest
235
+ self.version = version
236
+ self._sha_to_path: dict[str, str] | None = None
237
+
238
+ @property
239
+ def content_shas(self) -> set[str]:
240
+ return set(self.manifest.values())
241
+
242
+ @property
243
+ def sha_to_path(self) -> dict[str, str]:
244
+ if self._sha_to_path is None:
245
+ self._sha_to_path = {sha: path for path, sha in self.manifest.items()}
246
+ return self._sha_to_path
247
+
248
+
249
+ async def get_branch_context(
250
+ repo_id: uuid.UUID,
251
+ branch_name: str,
252
+ session: AsyncSession,
253
+ ) -> BranchContext:
254
+ """Resolve branch context for a query. Returns BranchContext with manifest.
255
+
256
+ If branch_name is empty, uses the repository's default branch.
257
+ """
258
+ from sqlalchemy import select
259
+
260
+ from attocode_intel.db.models import Branch, Repository
261
+ from attocode_intel.storage.branch_overlay import BranchOverlay
262
+
263
+ if not branch_name:
264
+ result = await session.execute(
265
+ select(Repository.default_branch).where(Repository.id == repo_id)
266
+ )
267
+ branch_name = result.scalar_one_or_none() or "main"
268
+
269
+ result = await session.execute(
270
+ select(Branch).where(
271
+ Branch.repo_id == repo_id,
272
+ Branch.name == branch_name,
273
+ )
274
+ )
275
+ branch = result.scalar_one_or_none()
276
+ if branch is None:
277
+ raise HTTPException(status_code=404, detail=f"Branch '{branch_name}' not found")
278
+
279
+ overlay = BranchOverlay(session)
280
+ manifest = await overlay.resolve_manifest(branch.id)
281
+ version = await overlay.get_version(branch.id)
282
+
283
+ return BranchContext(
284
+ branch_id=branch.id,
285
+ branch_name=branch_name,
286
+ manifest=manifest,
287
+ version=version,
288
+ )
289
+
290
+
291
+ # --- Git manager ---
292
+
293
+
294
+ def get_git_manager() -> GitRepoManager:
295
+ """Return a GitRepoManager configured from the active config."""
296
+ from attocode_intel.git.manager import GitRepoManager
297
+
298
+ config = get_config()
299
+ return GitRepoManager(config.git_clone_dir, config.git_ssh_key_path)
300
+
301
+
302
+ # --- Service mode dependencies ---
303
+
304
+
305
+ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
306
+ """Yield an async DB session. Raises 503 if not in service mode."""
307
+ config = get_config()
308
+ if not config.is_service_mode:
309
+ raise HTTPException(status_code=503, detail="Service mode not enabled (DATABASE_URL not set)")
310
+ from attocode_intel.db.engine import get_session
311
+
312
+ async for session in get_session():
313
+ yield session
314
+
315
+
316
+ async def get_org_scoped_session(
317
+ auth: AuthContext,
318
+ session: AsyncSession,
319
+ ) -> AsyncSession:
320
+ """Set RLS context for org isolation, then return the session.
321
+
322
+ Uses parameterized SET to prevent SQL injection.
323
+ """
324
+ if auth.org_id:
325
+ from sqlalchemy import text
326
+ await session.execute(
327
+ text("SET LOCAL app.current_org_id = :org_id").bindparams(org_id=str(auth.org_id))
328
+ )
329
+ return session
330
+
331
+
332
+ async def get_repo_service(
333
+ org_id: uuid.UUID,
334
+ repo_id: uuid.UUID,
335
+ session: AsyncSession,
336
+ ) -> CodeIntelService:
337
+ """Look up a Repository from DB and return a CodeIntelService for its local_path."""
338
+ from sqlalchemy import select
339
+
340
+ from attocode_intel.db.models import Repository
341
+
342
+ result = await session.execute(
343
+ select(Repository).where(Repository.id == repo_id, Repository.org_id == org_id)
344
+ )
345
+ repo = result.scalar_one_or_none()
346
+ if repo is None:
347
+ raise HTTPException(status_code=404, detail="Repository not found")
348
+ path = repo.clone_path or repo.local_path
349
+ if not path:
350
+ raise HTTPException(status_code=422, detail="Repository has no local path or clone")
351
+ return CodeIntelService.get_instance(path, _config)
@@ -0,0 +1,13 @@
1
+ """Keep historical HTTP knowledge endpoints on the team's canonical store."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from attocode_intel.api.auth import resolve_auth
6
+ from attocode_intel.api.routes.operations import execute_operation
7
+
8
+
9
+ async def shared_operation(request, workspace, name, arguments):
10
+ if not request.app.state.config.is_service_mode:
11
+ return None
12
+ auth = await resolve_auth(request.headers.get("authorization"))
13
+ return await execute_operation(name, {"workspace": workspace, **arguments}, request, auth)
@@ -0,0 +1,51 @@
1
+ """Native Streamable HTTP MCP, authenticated before protocol dispatch."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
8
+ from starlette.responses import JSONResponse
9
+
10
+ from attocode_intel.gateway import OperationGateway, create_mcp_server
11
+ from attocode_intel.remote import RemoteResolver, remote_identity, remote_profile, remote_workspace
12
+
13
+
14
+ class MCPTransport:
15
+ def __init__(self, config):
16
+ self.config = config
17
+ self.gateway = OperationGateway(
18
+ config.project_dir,
19
+ os.environ.get("ATTOCODE_MCP_PROFILE", "full"),
20
+ RemoteResolver(config) if config.is_service_mode else None,
21
+ watch=os.environ.get("ATTOCODE_INTEL_WATCH", "1") != "0",
22
+ watch_debounce=int(os.environ.get("ATTOCODE_INTEL_WATCH_DEBOUNCE", "500")),
23
+ )
24
+ self.manager = StreamableHTTPSessionManager(
25
+ create_mcp_server(self.gateway), json_response=True, stateless=True
26
+ )
27
+
28
+ async def __call__(self, scope, receive, send):
29
+ from fastapi import HTTPException
30
+
31
+ from attocode_intel.api.auth import resolve_auth
32
+
33
+ if scope["type"] != "http":
34
+ return
35
+ headers = {k.decode().lower(): v.decode() for k, v in scope.get("headers", [])}
36
+ try:
37
+ auth = await resolve_auth(headers.get("authorization"))
38
+ except HTTPException as exc:
39
+ await JSONResponse({"detail": exc.detail}, status_code=exc.status_code)(
40
+ scope, receive, send
41
+ )
42
+ return
43
+ token = remote_identity.set(auth)
44
+ workspace_token = remote_workspace.set(headers.get("x-attocode-workspace", ""))
45
+ profile_token = remote_profile.set(headers.get("x-attocode-profile", self.gateway.profile))
46
+ try:
47
+ await self.manager.handle_request(scope, receive, send)
48
+ finally:
49
+ remote_identity.reset(token)
50
+ remote_workspace.reset(workspace_token)
51
+ remote_profile.reset(profile_token)