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,100 @@
1
+ """User preferences endpoints (service mode only)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import APIRouter, Depends, HTTPException
6
+ from pydantic import BaseModel
7
+ from sqlalchemy import select
8
+ from sqlalchemy.ext.asyncio import AsyncSession
9
+
10
+ from attocode_intel.api.auth import resolve_auth
11
+ from attocode_intel.api.auth.context import AuthContext
12
+ from attocode_intel.api.deps import get_db_session
13
+
14
+ router = APIRouter(prefix="/api/v2/me", tags=["preferences"])
15
+
16
+
17
+ class PreferencesResponse(BaseModel):
18
+ preferences: dict
19
+
20
+
21
+ class PreferencesUpdateRequest(BaseModel):
22
+ preferences: dict
23
+
24
+
25
+ # --- Endpoints ---
26
+
27
+
28
+ @router.get("/preferences", response_model=PreferencesResponse)
29
+ async def get_preferences(
30
+ auth: AuthContext = Depends(resolve_auth),
31
+ session: AsyncSession = Depends(get_db_session),
32
+ ) -> PreferencesResponse:
33
+ """Get current user's preferences."""
34
+ if not auth.user_id:
35
+ raise HTTPException(status_code=401, detail="Authentication required")
36
+
37
+ from attocode_intel.db.models import UserPreference
38
+
39
+ result = await session.execute(
40
+ select(UserPreference).where(UserPreference.user_id == auth.user_id)
41
+ )
42
+ pref = result.scalar_one_or_none()
43
+ return PreferencesResponse(preferences=pref.preferences if pref else {})
44
+
45
+
46
+ @router.put("/preferences", response_model=PreferencesResponse)
47
+ async def replace_preferences(
48
+ req: PreferencesUpdateRequest,
49
+ auth: AuthContext = Depends(resolve_auth),
50
+ session: AsyncSession = Depends(get_db_session),
51
+ ) -> PreferencesResponse:
52
+ """Full replace of user preferences."""
53
+ if not auth.user_id:
54
+ raise HTTPException(status_code=401, detail="Authentication required")
55
+
56
+ from attocode_intel.db.models import UserPreference
57
+
58
+ result = await session.execute(
59
+ select(UserPreference).where(UserPreference.user_id == auth.user_id)
60
+ )
61
+ pref = result.scalar_one_or_none()
62
+
63
+ if pref:
64
+ pref.preferences = req.preferences
65
+ else:
66
+ pref = UserPreference(user_id=auth.user_id, preferences=req.preferences)
67
+ session.add(pref)
68
+
69
+ await session.commit()
70
+ await session.refresh(pref)
71
+ return PreferencesResponse(preferences=pref.preferences)
72
+
73
+
74
+ @router.patch("/preferences", response_model=PreferencesResponse)
75
+ async def merge_preferences(
76
+ req: PreferencesUpdateRequest,
77
+ auth: AuthContext = Depends(resolve_auth),
78
+ session: AsyncSession = Depends(get_db_session),
79
+ ) -> PreferencesResponse:
80
+ """Merge update of user preferences (shallow merge)."""
81
+ if not auth.user_id:
82
+ raise HTTPException(status_code=401, detail="Authentication required")
83
+
84
+ from attocode_intel.db.models import UserPreference
85
+
86
+ result = await session.execute(
87
+ select(UserPreference).where(UserPreference.user_id == auth.user_id)
88
+ )
89
+ pref = result.scalar_one_or_none()
90
+
91
+ if pref:
92
+ merged = {**pref.preferences, **req.preferences}
93
+ pref.preferences = merged
94
+ else:
95
+ pref = UserPreference(user_id=auth.user_id, preferences=req.preferences)
96
+ session.add(pref)
97
+
98
+ await session.commit()
99
+ await session.refresh(pref)
100
+ return PreferencesResponse(preferences=pref.preferences)
@@ -0,0 +1,42 @@
1
+ """Presence tracking REST endpoints (service mode only)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+
7
+ from fastapi import APIRouter, Depends
8
+ from pydantic import BaseModel
9
+
10
+ from attocode_intel.api.auth import resolve_auth
11
+ from attocode_intel.api.auth.context import AuthContext
12
+
13
+ router = APIRouter(prefix="/api/v2/repos/{repo_id}", tags=["presence"])
14
+
15
+
16
+ class PresenceEntryResponse(BaseModel):
17
+ user_id: str
18
+ user_name: str
19
+ file: str
20
+ session_id: str
21
+ timestamp: float
22
+
23
+
24
+ @router.get("/presence", response_model=list[PresenceEntryResponse])
25
+ async def get_repo_presence(
26
+ repo_id: uuid.UUID,
27
+ auth: AuthContext = Depends(resolve_auth),
28
+ ) -> list[PresenceEntryResponse]:
29
+ """Get current viewers of a repository (REST fallback for non-WebSocket clients)."""
30
+ from attocode_intel.presence import get_presence
31
+
32
+ entries = await get_presence(str(repo_id))
33
+ return [
34
+ PresenceEntryResponse(
35
+ user_id=e.user_id,
36
+ user_name=e.user_name,
37
+ file=e.file,
38
+ session_id=e.session_id,
39
+ timestamp=e.timestamp,
40
+ )
41
+ for e in entries
42
+ ]
@@ -0,0 +1,116 @@
1
+ """Project management endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import os
7
+ import uuid
8
+
9
+ from fastapi import APIRouter, Depends, HTTPException
10
+
11
+ from attocode_intel.api.auth import verify_auth
12
+ from attocode_intel.api.deps import (
13
+ get_config,
14
+ get_service_or_404,
15
+ list_projects,
16
+ register_project,
17
+ )
18
+ from fastapi import Query as QueryParam
19
+
20
+ from attocode_intel.api.models import (
21
+ ProjectInfo,
22
+ ProjectListResponse,
23
+ ProjectRegister,
24
+ TextResult,
25
+ )
26
+
27
+ router = APIRouter(prefix="/api/v1/projects", tags=["projects"], dependencies=[Depends(verify_auth)])
28
+
29
+ # Serializes reindex operations so concurrent requests don't corrupt state
30
+ _reindex_lock = asyncio.Lock()
31
+
32
+ # System directories that should never be registered as projects
33
+ _BLOCKED_PREFIXES = ("/etc", "/var", "/usr", "/sys", "/proc", "/dev")
34
+
35
+
36
+ @router.post("", response_model=ProjectInfo)
37
+ async def create_project(req: ProjectRegister) -> ProjectInfo:
38
+ """Register a project (local path)."""
39
+ abs_path = os.path.abspath(req.path)
40
+ if not os.path.isdir(abs_path):
41
+ raise HTTPException(status_code=400, detail=f"Directory not found: {abs_path}")
42
+
43
+ # Block system directories when API key auth is active
44
+ config = get_config()
45
+ if config.api_key:
46
+ for prefix in _BLOCKED_PREFIXES:
47
+ if abs_path == prefix or abs_path.startswith(prefix + "/"):
48
+ raise HTTPException(status_code=400, detail=f"Cannot register system directory: {abs_path}")
49
+
50
+ # Idempotent: return existing project if same path already registered
51
+ for pid, svc in list_projects().items():
52
+ if svc.project_dir == abs_path:
53
+ return ProjectInfo(
54
+ id=pid,
55
+ name=req.name or os.path.basename(abs_path),
56
+ path=abs_path,
57
+ status="ready",
58
+ )
59
+
60
+ project_id = str(uuid.uuid4())[:8]
61
+ name = req.name or os.path.basename(abs_path)
62
+ register_project(project_id, abs_path, name)
63
+
64
+ return ProjectInfo(
65
+ id=project_id,
66
+ name=name,
67
+ path=abs_path,
68
+ status="ready",
69
+ )
70
+
71
+
72
+ @router.get("", response_model=ProjectListResponse)
73
+ async def get_projects(
74
+ limit: int = QueryParam(20, ge=1, le=100),
75
+ offset: int = QueryParam(0, ge=0),
76
+ ) -> ProjectListResponse:
77
+ """List all registered projects."""
78
+ projects = list_projects()
79
+ all_items = []
80
+ for pid, svc in projects.items():
81
+ all_items.append(ProjectInfo(
82
+ id=pid,
83
+ name=os.path.basename(svc.project_dir),
84
+ path=svc.project_dir,
85
+ status="ready",
86
+ ))
87
+ total = len(all_items)
88
+ page = all_items[offset:offset + limit]
89
+ return ProjectListResponse(
90
+ projects=page, total=total, limit=limit, offset=offset,
91
+ has_more=(offset + limit < total),
92
+ )
93
+
94
+
95
+ @router.get("/{project_id}", response_model=ProjectInfo)
96
+ async def get_project(project_id: str) -> ProjectInfo:
97
+ """Get project details."""
98
+ svc = await get_service_or_404(project_id)
99
+ return ProjectInfo(
100
+ id=project_id,
101
+ name=os.path.basename(svc.project_dir),
102
+ path=svc.project_dir,
103
+ status="ready",
104
+ )
105
+
106
+
107
+ @router.post("/{project_id}/reindex", response_model=TextResult)
108
+ async def reindex_project(project_id: str) -> TextResult:
109
+ """Trigger a full reindex of the project."""
110
+ svc = await get_service_or_404(project_id)
111
+ async with _reindex_lock:
112
+ svc._ast_service = None
113
+ svc._context_mgr = None
114
+ svc._get_ast_service()
115
+ svc._get_context_mgr()
116
+ return TextResult(result="Reindex complete")
@@ -0,0 +1,47 @@
1
+ """Direct repo lookup endpoint (by repo_id, without org context)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+
7
+ from fastapi import APIRouter, Depends, HTTPException
8
+ from sqlalchemy import select
9
+ from sqlalchemy.ext.asyncio import AsyncSession
10
+
11
+ from attocode_intel.api.auth import resolve_auth
12
+ from attocode_intel.api.auth.context import AuthContext
13
+ from attocode_intel.api.deps import get_db_session
14
+ from attocode_intel.api.routes.orgs import RepoResponse
15
+ from attocode_intel.db.models import Repository
16
+
17
+ router = APIRouter(prefix="/api/v1/repos", tags=["repos"])
18
+
19
+
20
+ @router.get("/{repo_id}", response_model=RepoResponse)
21
+ async def get_repo_by_id(
22
+ repo_id: uuid.UUID,
23
+ auth: AuthContext = Depends(resolve_auth),
24
+ session: AsyncSession = Depends(get_db_session),
25
+ ) -> RepoResponse:
26
+ """Look up a repository directly by ID (without requiring org context)."""
27
+ result = await session.execute(
28
+ select(Repository).where(Repository.id == repo_id)
29
+ )
30
+ repo = result.scalar_one_or_none()
31
+ if repo is None:
32
+ raise HTTPException(status_code=404, detail="Repository not found")
33
+
34
+ # Org isolation: verify the repo belongs to the authenticated user's org
35
+ if auth.org_id and repo.org_id != auth.org_id:
36
+ raise HTTPException(status_code=404, detail="Repository not found")
37
+
38
+ return RepoResponse(
39
+ id=str(repo.id),
40
+ name=repo.name,
41
+ clone_url=repo.clone_url,
42
+ default_branch=repo.default_branch,
43
+ language=repo.language,
44
+ index_status=repo.index_status,
45
+ last_indexed_at=repo.last_indexed_at.isoformat() if repo.last_indexed_at else None,
46
+ created_at=repo.created_at.isoformat(),
47
+ )
@@ -0,0 +1,180 @@
1
+ """Search endpoints — unified v1 (text) + v2 (structured JSON)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from fastapi import APIRouter, Depends
8
+ from pydantic import BaseModel
9
+
10
+ from attocode_intel.api.auth import verify_auth
11
+ from attocode_intel.api.deps import (
12
+ BranchParam,
13
+ ensure_branch_supported,
14
+ get_search_provider,
15
+ get_service_or_404,
16
+ )
17
+ from attocode_intel.api.models import (
18
+ FastSearchRequest,
19
+ SearchResultsResponse,
20
+ SecurityScanRequest,
21
+ SecurityScanResponse,
22
+ SemanticSearchRequest,
23
+ TextResult,
24
+ )
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ # --- v1 router: text responses ---
29
+
30
+ router_v1 = APIRouter(
31
+ prefix="/api/v1/projects/{project_id}",
32
+ tags=["search"],
33
+ dependencies=[Depends(verify_auth)],
34
+ )
35
+
36
+ # --- v2 router: structured JSON ---
37
+
38
+ router_v2 = APIRouter(
39
+ prefix="/api/v2/projects/{project_id}",
40
+ tags=["search-v2"],
41
+ dependencies=[Depends(verify_auth)],
42
+ )
43
+
44
+
45
+ class IndexStatusResponse(BaseModel):
46
+ provider: str = ""
47
+ available: bool = False
48
+ status: str = "idle"
49
+ total_files: int = 0
50
+ indexed_files: int = 0
51
+ failed_files: int = 0
52
+ coverage: float = 0.0
53
+ elapsed_seconds: float = 0.0
54
+ vector_search_active: bool = False
55
+ health_status: str = "unknown"
56
+ degraded_reason: str = ""
57
+ last_error: str = ""
58
+
59
+
60
+ # ===================================================================
61
+ # v1 endpoints
62
+ # ===================================================================
63
+
64
+
65
+ @router_v1.post("/search", response_model=TextResult)
66
+ async def semantic_search_v1(
67
+ project_id: str,
68
+ req: SemanticSearchRequest,
69
+ branch: BranchParam = "",
70
+ ) -> TextResult:
71
+ """Semantic search (vector + keyword RRF)."""
72
+ ensure_branch_supported(branch)
73
+ svc = await get_service_or_404(project_id)
74
+ return TextResult(result=svc.semantic_search(
75
+ query=req.query, top_k=req.top_k, file_filter=req.file_filter,
76
+ ))
77
+
78
+
79
+ @router_v1.post("/index", response_model=IndexStatusResponse)
80
+ async def start_indexing(
81
+ project_id: str,
82
+ branch: BranchParam = "",
83
+ ) -> IndexStatusResponse:
84
+ """Start background embedding indexing."""
85
+ ensure_branch_supported(branch)
86
+ svc = await get_service_or_404(project_id)
87
+ result = svc.start_indexing()
88
+ return IndexStatusResponse(**result)
89
+
90
+
91
+ @router_v1.get("/index/status", response_model=IndexStatusResponse)
92
+ async def index_status(
93
+ project_id: str,
94
+ branch: BranchParam = "",
95
+ ) -> IndexStatusResponse:
96
+ """Get current embedding index status."""
97
+ ensure_branch_supported(branch)
98
+ svc = await get_service_or_404(project_id)
99
+ result = svc.indexing_status()
100
+ return IndexStatusResponse(**result)
101
+
102
+
103
+ @router_v1.get("/semantic-search-status", response_model=TextResult)
104
+ async def semantic_search_status_v1(
105
+ project_id: str,
106
+ branch: BranchParam = "",
107
+ ) -> TextResult:
108
+ """Human-readable semantic-search health."""
109
+ ensure_branch_supported(branch)
110
+ svc = await get_service_or_404(project_id)
111
+ return TextResult(result=svc.semantic_search_status())
112
+
113
+
114
+ @router_v1.post("/fast-search", response_model=TextResult)
115
+ async def fast_search_v1(
116
+ project_id: str,
117
+ req: FastSearchRequest,
118
+ branch: BranchParam = "",
119
+ ) -> TextResult:
120
+ """Regex search with trigram acceleration when available."""
121
+ ensure_branch_supported(branch)
122
+ svc = await get_service_or_404(project_id)
123
+ return TextResult(result=svc.fast_search(
124
+ pattern=req.pattern,
125
+ path=req.path,
126
+ max_results=req.max_results,
127
+ case_insensitive=req.case_insensitive,
128
+ selectivity_threshold=req.selectivity_threshold,
129
+ explain=req.explain,
130
+ ))
131
+
132
+
133
+ # ===================================================================
134
+ # v2 endpoints
135
+ # ===================================================================
136
+
137
+
138
+ @router_v2.post("/search", response_model=SearchResultsResponse)
139
+ async def semantic_search_v2(
140
+ project_id: str,
141
+ req: SemanticSearchRequest,
142
+ branch: BranchParam = "",
143
+ ) -> SearchResultsResponse:
144
+ """Semantic search (structured).
145
+
146
+ In service mode with pgvector, embeds the query and runs cosine similarity search.
147
+ Falls back to the local service's text-based search otherwise.
148
+ """
149
+ from fastapi import HTTPException
150
+
151
+ try:
152
+ provider = await get_search_provider(project_id)
153
+ return await provider.semantic_search(
154
+ query=req.query, top_k=req.top_k, file_filter=req.file_filter, branch=branch,
155
+ )
156
+ except (RuntimeError, ImportError) as exc:
157
+ logger.warning("Search unavailable for project %s: %s", project_id, exc)
158
+ raise HTTPException(
159
+ status_code=503,
160
+ detail=f"Search is not available: {exc}",
161
+ ) from exc
162
+ except HTTPException:
163
+ raise
164
+ except Exception as exc:
165
+ logger.exception("Search failed for project %s", project_id)
166
+ raise HTTPException(
167
+ status_code=500,
168
+ detail=f"Search failed: {exc}",
169
+ ) from exc
170
+
171
+
172
+ @router_v2.post("/security-scan", response_model=SecurityScanResponse)
173
+ async def security_scan_v2(
174
+ project_id: str,
175
+ req: SecurityScanRequest,
176
+ branch: BranchParam = "",
177
+ ) -> SecurityScanResponse:
178
+ """Security analysis (structured)."""
179
+ provider = await get_search_provider(project_id)
180
+ return await provider.security_scan(mode=req.mode, path=req.path, branch=branch)