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,51 @@
1
+ """Redis connection management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from redis.asyncio import Redis
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ _redis: Redis | None = None
14
+
15
+
16
+ async def get_redis() -> Redis:
17
+ """Get or create the Redis connection singleton."""
18
+ global _redis
19
+ if _redis is not None:
20
+ return _redis
21
+
22
+ from attocode_intel.api.deps import get_config
23
+
24
+ config = get_config()
25
+ if not config.redis_url:
26
+ raise RuntimeError("REDIS_URL not configured")
27
+
28
+ from redis.asyncio import Redis as AsyncRedis
29
+
30
+ _redis = AsyncRedis.from_url(config.redis_url, decode_responses=True)
31
+ logger.info("Redis connection initialized")
32
+ return _redis
33
+
34
+
35
+ async def close_redis() -> None:
36
+ """Close the Redis connection."""
37
+ global _redis
38
+ if _redis is not None:
39
+ await _redis.aclose()
40
+ _redis = None
41
+ logger.info("Redis connection closed")
42
+
43
+
44
+ async def health_check() -> bool:
45
+ """Check if Redis is reachable."""
46
+ try:
47
+ r = await get_redis()
48
+ await r.ping()
49
+ return True
50
+ except Exception:
51
+ return False
@@ -0,0 +1,60 @@
1
+ """Workspace-scoped reference continuations over immutable result snapshots."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import secrets
6
+ from collections import OrderedDict
7
+ from copy import deepcopy
8
+
9
+ from attocode_intel.output import bounded_compact, compact_result
10
+
11
+
12
+ def scope(context, args):
13
+ tracker = context.stores.get("freshness")
14
+ return (context.workspace, context.source, context.revision, getattr(tracker, "generation", 0),
15
+ args["symbol_name"], args.get("file_path"), args.get("line"))
16
+
17
+
18
+ def continuation(context, args):
19
+ cursor = args.get("cursor")
20
+ if not cursor:
21
+ return None
22
+ pages = context.stores.get("reference_pages", {})
23
+ record = pages.get(cursor)
24
+ if record is None or record["scope"] != scope(context, args):
25
+ raise ValueError("Reference cursor expired or belongs to another query; restart without cursor")
26
+ return record
27
+
28
+
29
+ def paginate(context, args, payload, metadata, budget, compact):
30
+ size = args.get("page_size") if args.get("page_size") is not None else 20
31
+ if not 1 <= size <= 100:
32
+ raise ValueError("page_size must be between 1 and 100")
33
+ record = continuation(context, args)
34
+ offset = record["offset"] if record else 0
35
+ if record:
36
+ payload, metadata = record["payload"], record["metadata"]
37
+ rows = payload["references"]
38
+ token = secrets.token_hex(16)
39
+ page = {**payload, "references": rows[offset:offset + size],
40
+ "next_cursor": token, "total_references": len(rows)}
41
+ metadata = {**metadata, "truncated": offset + size < len(rows)}
42
+ if compact:
43
+ rendered = json.loads(bounded_compact(metadata, page, budget).content[0].text)
44
+ page = rendered["data"]
45
+ compact_meta = rendered["metadata"]
46
+ count = len(page["references"])
47
+ if count == 0 and offset < len(rows):
48
+ raise ValueError("max_tokens is too small for one reference and provenance; increase it")
49
+ more = offset + count < len(rows)
50
+ page["next_cursor"] = token if more else None
51
+ if more:
52
+ pages = context.stores.setdefault("reference_pages", OrderedDict())
53
+ pages[token] = {"scope": scope(context, args), "offset": offset + count,
54
+ "payload": payload, "metadata": metadata}
55
+ while len(pages) > 64:
56
+ pages.popitem(last=False)
57
+ if compact:
58
+ compact_meta["truncated"] = compact_meta.get("truncated", False) or more
59
+ return compact_result(compact_meta, page)
60
+ return deepcopy(page), metadata
@@ -0,0 +1,131 @@
1
+ """Authenticated repository selection and immutable Git snapshots."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import subprocess
7
+ import uuid
8
+ from contextvars import ContextVar
9
+ from pathlib import Path
10
+
11
+ from filelock import FileLock
12
+
13
+ from attocode_intel.catalog import is_write, remote_available
14
+ from attocode_intel.request_context import RequestContext
15
+
16
+ remote_workspace: ContextVar[str] = ContextVar("remote_workspace", default="")
17
+ remote_profile: ContextVar[str] = ContextVar("remote_profile", default="full")
18
+
19
+ remote_identity: ContextVar[object | None] = ContextVar("intelligence_identity", default=None)
20
+
21
+
22
+ async def authorized_repo(repo_id: str, auth, *, write: bool = False):
23
+ from fastapi import HTTPException
24
+ from sqlalchemy import select
25
+
26
+ from attocode_intel.db.engine import get_session
27
+ from attocode_intel.db.models import OrgMembership, Repository
28
+
29
+ try:
30
+ rid = uuid.UUID(repo_id)
31
+ except ValueError as exc:
32
+ raise HTTPException(
33
+ 404, "Repository not found; supply workspace as a repository ID"
34
+ ) from exc
35
+ if auth is None or auth.auth_method == "legacy":
36
+ raise HTTPException(401, "Authentication required")
37
+ required = "write" if write else "read"
38
+ if (
39
+ auth.auth_method == "api_key"
40
+ and required not in auth.scopes
41
+ and f"intelligence:{required}" not in auth.scopes
42
+ ):
43
+ raise HTTPException(403, f"API key requires intelligence:{required} scope")
44
+ async for session in get_session():
45
+ repo = (
46
+ await session.execute(select(Repository).where(Repository.id == rid))
47
+ ).scalar_one_or_none()
48
+ if repo is None or (auth.org_id and auth.org_id != repo.org_id):
49
+ raise HTTPException(404, "Repository not found")
50
+ if auth.user_id:
51
+ membership = (
52
+ await session.execute(
53
+ select(OrgMembership).where(
54
+ OrgMembership.org_id == repo.org_id,
55
+ OrgMembership.user_id == auth.user_id,
56
+ OrgMembership.accepted_at.is_not(None),
57
+ )
58
+ )
59
+ ).scalar_one_or_none()
60
+ if membership is None:
61
+ raise HTTPException(404, "Repository not found")
62
+ elif auth.auth_method != "api_key" or auth.org_id != repo.org_id:
63
+ raise HTTPException(404, "Repository not found")
64
+ return repo
65
+ raise HTTPException(503, "Repository database unavailable")
66
+
67
+
68
+ def committed_snapshot(
69
+ repo_path: str, cache_root: str, repo_id: str, revision: str
70
+ ) -> tuple[str, str]:
71
+ """Create a detached worktree; never change the source checkout or upload edits."""
72
+
73
+ def git(*args):
74
+ result = subprocess.run(
75
+ [
76
+ "git",
77
+ "-c",
78
+ "core.hooksPath=/dev/null",
79
+ "-c",
80
+ "core.fsmonitor=false",
81
+ "-C",
82
+ repo_path,
83
+ *args,
84
+ ],
85
+ capture_output=True,
86
+ text=True,
87
+ timeout=90,
88
+ check=True,
89
+ )
90
+ return result.stdout.strip()
91
+
92
+ commit = git("rev-parse", "--verify", "--end-of-options", f"{revision}^{{commit}}")
93
+ base = Path(cache_root) / ".intelligence-snapshots" / repo_id
94
+ base.mkdir(parents=True, exist_ok=True)
95
+ target = base / commit
96
+ with FileLock(str(base / "snapshots.lock"), timeout=90):
97
+ if not (target / ".git").exists():
98
+ git("worktree", "add", "--detach", str(target), commit)
99
+ # Prevent repository symlinks from exposing files outside the snapshot.
100
+ for path in target.rglob("*"):
101
+ if path.is_symlink() and not path.resolve().is_relative_to(target.resolve()):
102
+ raise ValueError(
103
+ f"Snapshot contains an external symlink: {path.relative_to(target)}"
104
+ )
105
+ return str(target.resolve()), commit
106
+
107
+
108
+ class RemoteResolver:
109
+ def __init__(self, config):
110
+ self.config = config
111
+
112
+ async def __call__(self, workspace, revision, name):
113
+ workspace = workspace or remote_workspace.get()
114
+ auth = remote_identity.get()
115
+ write = is_write(name)
116
+ repo = await authorized_repo(workspace, auth, write=write)
117
+ if not remote_available(name):
118
+ raise ValueError(
119
+ "This operation is local-only; remote source snapshots cannot be modified."
120
+ )
121
+ path = repo.clone_path or repo.local_path
122
+ if not path:
123
+ raise ValueError("Repository has not been cloned or indexed yet")
124
+ path, commit = await asyncio.to_thread(
125
+ committed_snapshot,
126
+ path,
127
+ self.config.git_clone_dir,
128
+ str(repo.id),
129
+ revision or repo.default_branch,
130
+ )
131
+ return RequestContext(path, str(repo.id), commit, "remote", auth)
@@ -0,0 +1,246 @@
1
+ """Graph-ranked repo map using PageRank.
2
+
3
+ Ranks files and symbols by their connectivity in the
4
+ dependency graph, weighted by task relevance. Produces
5
+ a token-budgeted summary of the most important code.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+
12
+ from attocode_intel.focused_evidence import task_terms
13
+
14
+
15
+ @dataclass(slots=True)
16
+ class RankedEntry:
17
+ """A ranked file or symbol in the repo map."""
18
+ path: str
19
+ score: float
20
+ symbols: list[str] = field(default_factory=list)
21
+ line_count: int = 0
22
+ category: str = "" # "core", "util", "test", etc.
23
+
24
+
25
+ @dataclass(slots=True)
26
+ class RepoMapResult:
27
+ """Result of repo map ranking."""
28
+ entries: list[RankedEntry]
29
+ total_files: int
30
+ token_budget: int
31
+ tokens_used: int
32
+ truncated: bool = False
33
+
34
+
35
+ def pagerank(
36
+ adjacency: dict[str, list[str]],
37
+ *,
38
+ damping: float = 0.85,
39
+ iterations: int = 30,
40
+ tolerance: float = 1e-6,
41
+ ) -> dict[str, float]:
42
+ """Compute PageRank scores for nodes in a directed graph.
43
+
44
+ Args:
45
+ adjacency: Maps each node to its outgoing edges (dependencies).
46
+ damping: Damping factor (probability of following a link).
47
+ iterations: Maximum iterations.
48
+ tolerance: Convergence threshold.
49
+
50
+ Returns:
51
+ Dict mapping node -> PageRank score.
52
+ """
53
+ nodes = set(adjacency.keys())
54
+ for targets in adjacency.values():
55
+ nodes.update(targets)
56
+
57
+ if not nodes:
58
+ return {}
59
+
60
+ n = len(nodes)
61
+ node_list = sorted(nodes)
62
+ node_idx = {node: i for i, node in enumerate(node_list)}
63
+
64
+ # Build reverse adjacency (who points to me?)
65
+ in_links: dict[int, list[int]] = {i: [] for i in range(n)}
66
+ out_degree: dict[int, int] = {i: 0 for i in range(n)}
67
+
68
+ for source, targets in adjacency.items():
69
+ src_idx = node_idx[source]
70
+ valid_targets = [t for t in targets if t in node_idx]
71
+ out_degree[src_idx] = len(valid_targets)
72
+ for target in valid_targets:
73
+ in_links[node_idx[target]].append(src_idx)
74
+
75
+ # Initialize uniform
76
+ scores = [1.0 / n] * n
77
+ base = (1.0 - damping) / n
78
+
79
+ # Identify dangling nodes (no outgoing edges)
80
+ dangling = [i for i in range(n) if out_degree[i] == 0]
81
+
82
+ for _ in range(iterations):
83
+ # Redistribute dangling node rank uniformly
84
+ dangling_sum = sum(scores[i] for i in dangling)
85
+ new_scores = [0.0] * n
86
+ for i in range(n):
87
+ rank_sum = sum(
88
+ scores[j] / out_degree[j]
89
+ for j in in_links[i]
90
+ if out_degree[j] > 0
91
+ )
92
+ new_scores[i] = base + damping * (rank_sum + dangling_sum / n)
93
+
94
+ # Check convergence
95
+ diff = sum(abs(new_scores[i] - scores[i]) for i in range(n))
96
+ scores = new_scores
97
+ if diff < tolerance:
98
+ break
99
+
100
+ return {node_list[i]: scores[i] for i in range(n)}
101
+
102
+
103
+ def _task_relevance(path: str, task_keywords: set[str], excluded_keywords: set[str]) -> float:
104
+ """Compute task relevance score for a file path."""
105
+ if not task_keywords:
106
+ return 1.0
107
+ path_lower = path.lower()
108
+ matches = sum(1 for kw in task_keywords if kw.lower() in path_lower)
109
+ relevance = (0.1 if matches == 0
110
+ else min(1.0, 0.3 + 0.7 * (matches / len(task_keywords))))
111
+ if any(kw in path_lower for kw in excluded_keywords):
112
+ relevance *= 0.2 # An explicit exclusion is a hint, not a hard filter.
113
+ return relevance
114
+
115
+
116
+ def _estimate_entry_tokens(entry: RankedEntry) -> int:
117
+ """Estimate tokens for a ranked entry in the output."""
118
+ # path + symbols list
119
+ tokens = len(entry.path.split("/")) + 2
120
+ tokens += sum(len(s.split()) + 1 for s in entry.symbols)
121
+ return max(tokens, 3)
122
+
123
+
124
+ def _categorize_path(path: str) -> str:
125
+ """Categorize a file path."""
126
+ parts = path.lower().split("/")
127
+ if any(p in ("test", "tests", "spec", "specs") for p in parts):
128
+ return "test"
129
+ if any(p in ("util", "utils", "helpers", "lib") for p in parts):
130
+ return "util"
131
+ if any(p in ("core", "engine", "kernel") for p in parts):
132
+ return "core"
133
+ if any(p in ("api", "routes", "handlers", "views") for p in parts):
134
+ return "api"
135
+ if any(p in ("config", "settings", "conf") for p in parts):
136
+ return "config"
137
+ return "module"
138
+
139
+
140
+ def rank_repo_files(
141
+ adjacency: dict[str, list[str]],
142
+ *,
143
+ task_context: str = "",
144
+ token_budget: int = 1024,
145
+ symbols_by_file: dict[str, list[str]] | None = None,
146
+ line_counts: dict[str, int] | None = None,
147
+ exclude_tests: bool = True,
148
+ relevance_by_file: dict[str, float] | None = None,
149
+ ) -> RepoMapResult:
150
+ """Rank repository files by graph importance and task relevance.
151
+
152
+ Combines PageRank connectivity score with task-context keyword
153
+ relevance to produce a token-budgeted list of the most important
154
+ files and their key symbols.
155
+
156
+ Args:
157
+ adjacency: File dependency graph (file -> [imported files]).
158
+ task_context: Description of the current task for relevance scoring.
159
+ token_budget: Maximum tokens for the output.
160
+ symbols_by_file: Optional mapping of file -> symbol names.
161
+ line_counts: Optional mapping of file -> line count.
162
+ exclude_tests: Whether to exclude test files from ranking.
163
+
164
+ Returns:
165
+ RepoMapResult with ranked entries within token budget.
166
+ """
167
+ symbols_by_file = symbols_by_file or {}
168
+ line_counts = line_counts or {}
169
+
170
+ # Compute PageRank
171
+ pr_scores = pagerank(adjacency)
172
+
173
+ # Query-conditioned source evidence is stronger than path words, but graph
174
+ # importance still supplies a useful fallback for files outside the pool.
175
+ task_keywords, excluded_keywords = task_terms(task_context)
176
+ relevance_by_file = relevance_by_file or {}
177
+ max_pr = max(pr_scores.values(), default=1.0) or 1.0
178
+ max_relevance = max(relevance_by_file.values(), default=1.0) or 1.0
179
+
180
+ # Score each file
181
+ scored: list[tuple[str, float]] = []
182
+ for path, pr_score in pr_scores.items():
183
+ if exclude_tests and _categorize_path(path) == "test":
184
+ continue
185
+ relevance = _task_relevance(path, task_keywords, excluded_keywords)
186
+ if task_context and relevance_by_file:
187
+ source_relevance = relevance_by_file.get(path, 0.0) / max_relevance
188
+ combined = 0.70 * source_relevance + 0.30 * (pr_score / max_pr) * relevance
189
+ else:
190
+ combined = pr_score * relevance
191
+ scored.append((path, combined))
192
+
193
+ # Sort by combined score descending
194
+ scored.sort(key=lambda x: x[1], reverse=True)
195
+
196
+ # Build entries within token budget
197
+ entries: list[RankedEntry] = []
198
+ tokens_used = 0
199
+ truncated = False
200
+
201
+ for path, score in scored:
202
+ entry = RankedEntry(
203
+ path=path,
204
+ score=score,
205
+ symbols=symbols_by_file.get(path, [])[:10], # Cap symbols per file
206
+ line_count=line_counts.get(path, 0),
207
+ category=_categorize_path(path),
208
+ )
209
+ entry_tokens = _estimate_entry_tokens(entry)
210
+ if tokens_used + entry_tokens > token_budget:
211
+ truncated = True
212
+ break
213
+ entries.append(entry)
214
+ tokens_used += entry_tokens
215
+
216
+ return RepoMapResult(
217
+ entries=entries,
218
+ total_files=len(pr_scores),
219
+ token_budget=token_budget,
220
+ tokens_used=tokens_used,
221
+ truncated=truncated,
222
+ )
223
+
224
+
225
+ def format_repo_map(result: RepoMapResult) -> str:
226
+ """Format a RepoMapResult as a human-readable string."""
227
+ if not result.entries:
228
+ return "No files ranked."
229
+
230
+ lines = [f"Repo map ({len(result.entries)}/{result.total_files} files, ~{result.tokens_used} tokens):"]
231
+ lines.append("")
232
+
233
+ for entry in result.entries:
234
+ score_pct = entry.score * 100
235
+ prefix = f" [{entry.category:>6}] {entry.path}"
236
+ if entry.symbols:
237
+ syms = ", ".join(entry.symbols[:5])
238
+ if len(entry.symbols) > 5:
239
+ syms += f" (+{len(entry.symbols) - 5})"
240
+ prefix += f" ({syms})"
241
+ lines.append(f"{prefix} [{score_pct:.1f}%]")
242
+
243
+ if result.truncated:
244
+ lines.append(f"\n ... {result.total_files - len(result.entries)} more files omitted (token budget)")
245
+
246
+ return "\n".join(lines)
@@ -0,0 +1,82 @@
1
+ """Identity and workspace selection for one intelligence operation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from contextlib import contextmanager
6
+ from contextvars import ContextVar
7
+ from dataclasses import dataclass, field
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class RequestContext:
14
+ project_dir: str
15
+ workspace: str
16
+ revision: str = "working-tree"
17
+ source: str = "local"
18
+ identity: Any = None
19
+ stores: dict[str, Any] = field(default_factory=dict, compare=False)
20
+
21
+ @property
22
+ def service(self):
23
+ from attocode_intel.service import CodeIntelService
24
+
25
+ if "service" not in self.stores:
26
+ self.stores["service"] = CodeIntelService(self.project_dir)
27
+ return self.stores["service"]
28
+
29
+
30
+ current_request: ContextVar[RequestContext | None] = ContextVar(
31
+ "intelligence_request", default=None
32
+ )
33
+
34
+
35
+ @contextmanager
36
+ def bind_request(context: RequestContext):
37
+ token = current_request.set(context)
38
+ try:
39
+ yield context
40
+ finally:
41
+ current_request.reset(token)
42
+
43
+
44
+ def scoped_store(name, factory):
45
+ context = current_request.get()
46
+ if context is None:
47
+ return None
48
+ if name not in context.stores:
49
+ context.stores[name] = factory(context.project_dir)
50
+ return context.stores[name]
51
+
52
+
53
+ def resolve_workspace(workspace: str = "", default: str = "") -> str:
54
+ """Explicit roots win; discovery recognizes .git files in worktrees.
55
+
56
+ Discovery never selects the home directory. ``~/.attocode`` holds
57
+ user-wide settings, and a server that took it as a project marker
58
+ indexed all of home. Use ``--project ~`` to select home on purpose.
59
+ """
60
+ if workspace or default:
61
+ path = Path(workspace or default).expanduser().resolve()
62
+ if not path.is_dir():
63
+ raise ValueError(f"Workspace directory does not exist: {path}")
64
+ return str(path)
65
+ cwd = Path.cwd().resolve()
66
+ home = Path.home().resolve()
67
+ for path in (cwd, *cwd.parents):
68
+ if path != home and any(
69
+ (path / marker).exists()
70
+ for marker in (
71
+ ".git",
72
+ ".attocode",
73
+ "pyproject.toml",
74
+ "package.json",
75
+ "Cargo.toml",
76
+ "go.mod",
77
+ )
78
+ ):
79
+ return str(path)
80
+ raise ValueError(
81
+ "No repository selected. Supply workspace or start with --project /path/to/repo."
82
+ )
@@ -0,0 +1,28 @@
1
+ """Pluggable rule-based analysis engine.
2
+
3
+ Provides a unified rule model, registry, loader, executor, enricher,
4
+ and formatter for multi-language static analysis with language packs,
5
+ user plugins, and deterministic pre-filtering.
6
+ """
7
+
8
+ from attocode_intel.rules.model import (
9
+ AutoFix,
10
+ EnrichedFinding,
11
+ FewShotExample,
12
+ RuleCategory,
13
+ RuleSeverity,
14
+ RuleSource,
15
+ RuleTier,
16
+ UnifiedRule,
17
+ )
18
+
19
+ __all__ = [
20
+ "AutoFix",
21
+ "EnrichedFinding",
22
+ "FewShotExample",
23
+ "RuleCategory",
24
+ "RuleSeverity",
25
+ "RuleSource",
26
+ "RuleTier",
27
+ "UnifiedRule",
28
+ ]