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,198 @@
1
+ """Finding formatter — produces agent-optimized text output."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections import Counter
6
+
7
+ from attocode_intel.rules.model import (
8
+ EnrichedFinding,
9
+ FewShotExample,
10
+ RuleSeverity,
11
+ )
12
+
13
+
14
+ def format_findings(
15
+ findings: list[EnrichedFinding],
16
+ *,
17
+ max_findings: int = 50,
18
+ include_context: bool = True,
19
+ include_examples: bool = True,
20
+ ) -> str:
21
+ """Format findings as agent-optimized markdown text.
22
+
23
+ Designed to give the connected coding agent everything it needs
24
+ to triage, fix, and explain the findings to the user.
25
+ """
26
+ if not findings:
27
+ return "No findings detected."
28
+
29
+ sev_counts = Counter(f.severity for f in findings)
30
+ file_count = len(set(f.file for f in findings))
31
+ parts = []
32
+ for sev in (RuleSeverity.CRITICAL, RuleSeverity.HIGH, RuleSeverity.MEDIUM, RuleSeverity.LOW, RuleSeverity.INFO):
33
+ c = sev_counts.get(sev, 0)
34
+ if c:
35
+ parts.append(f"{c} {sev}")
36
+ summary = ", ".join(parts)
37
+
38
+ lines: list[str] = []
39
+ lines.append(f"## Analysis: {len(findings)} findings ({summary}) across {file_count} files")
40
+ lines.append("")
41
+
42
+ shown = findings[:max_findings]
43
+ if len(findings) > max_findings:
44
+ lines.append(f"_Showing top {max_findings} of {len(findings)} findings._\n")
45
+
46
+ for finding in shown:
47
+ lines.append(_format_single(finding, include_context=include_context, include_examples=include_examples))
48
+ lines.append("")
49
+
50
+ return "\n".join(lines)
51
+
52
+
53
+ def _format_single(
54
+ f: EnrichedFinding,
55
+ *,
56
+ include_context: bool = True,
57
+ include_examples: bool = True,
58
+ ) -> str:
59
+ lines: list[str] = []
60
+
61
+ # Header
62
+ sev_tag = f.severity.upper()
63
+ lines.append(f"### [{sev_tag}] {f.rule_id}")
64
+
65
+ # Location
66
+ loc = f"File: {f.file}:{f.line}"
67
+ if f.function_name:
68
+ loc += f" | in {f.function_name}()"
69
+ lines.append(loc)
70
+
71
+ # Metadata
72
+ meta_parts = [f"Category: {f.category}"]
73
+ meta_parts.append(f"Confidence: {f.confidence:.0%}")
74
+ if f.pack:
75
+ meta_parts.append(f"Pack: {f.pack}")
76
+ if f.cwe:
77
+ meta_parts.append(f.cwe)
78
+ lines.append(" | ".join(meta_parts))
79
+ lines.append("")
80
+
81
+ # Code context
82
+ if include_context and (f.context_before or f.context_after):
83
+ lines.append("```")
84
+ start_line = f.line - len(f.context_before)
85
+ for i, ctx_line in enumerate(f.context_before):
86
+ lines.append(f" {start_line + i:>4} | {ctx_line}")
87
+ lines.append(f"> {f.line:>4} | {f.code_snippet[:200]}")
88
+ for i, ctx_line in enumerate(f.context_after):
89
+ lines.append(f" {f.line + 1 + i:>4} | {ctx_line}")
90
+ lines.append("```")
91
+ elif f.code_snippet:
92
+ lines.append("```")
93
+ lines.append(f"> {f.line:>4} | {f.code_snippet[:200]}")
94
+ lines.append("```")
95
+ lines.append("")
96
+
97
+ # Description
98
+ if f.description:
99
+ lines.append(f"**What:** {f.description}")
100
+
101
+ # Explanation
102
+ if f.explanation:
103
+ lines.append(f"**Why this matters:** {f.explanation}")
104
+
105
+ # Recommendation
106
+ if f.recommendation:
107
+ lines.append(f"**Recommendation:** {f.recommendation}")
108
+
109
+ # Suggested fix
110
+ if f.suggested_fix:
111
+ lines.append("**Suggested fix:**")
112
+ lines.append("```")
113
+ lines.append(f"{f.suggested_fix}")
114
+ lines.append("```")
115
+
116
+ # Taint path
117
+ if f.taint_path:
118
+ lines.append(f"**Taint flow:** {f.taint_path}")
119
+
120
+ # Few-shot examples
121
+ if include_examples and f.examples:
122
+ for ex in f.examples[:2]:
123
+ lines.append(_format_example(ex))
124
+
125
+ # References
126
+ if f.references:
127
+ lines.append("**References:** " + ", ".join(f.references))
128
+
129
+ return "\n".join(lines)
130
+
131
+
132
+ def _format_example(ex: FewShotExample) -> str:
133
+ lines = ["**Example (bad -> good):**"]
134
+ lines.append(f" BAD: {ex.bad_code}")
135
+ lines.append(f" GOOD: {ex.good_code}")
136
+ if ex.explanation:
137
+ lines.append(f" Why: {ex.explanation}")
138
+ return "\n".join(lines)
139
+
140
+
141
+ def format_summary(findings: list[EnrichedFinding]) -> str:
142
+ """Brief summary without code context."""
143
+ return format_findings(findings, include_context=False, include_examples=False, max_findings=100)
144
+
145
+
146
+ def format_rules_list(
147
+ rules: list, # list[UnifiedRule] — loose typing to avoid circular import
148
+ *,
149
+ verbose: bool = False,
150
+ ) -> str:
151
+ """Format a list of rules for the list_rules MCP tool."""
152
+ if not rules:
153
+ return "No rules match the given filters."
154
+
155
+ lines: list[str] = []
156
+ lines.append(f"## Rules: {len(rules)} total\n")
157
+
158
+ by_cat: dict[str, list] = {}
159
+ for rule in rules:
160
+ cat = str(getattr(rule, "category", "unknown"))
161
+ by_cat.setdefault(cat, []).append(rule)
162
+
163
+ for cat in sorted(by_cat.keys()):
164
+ cat_rules = by_cat[cat]
165
+ lines.append(f"### {cat} ({len(cat_rules)})")
166
+ for rule in cat_rules:
167
+ sev = str(getattr(rule, "severity", "?"))
168
+ qid = getattr(rule, "qualified_id", getattr(rule, "id", "?"))
169
+ desc = getattr(rule, "description", "")[:80]
170
+ enabled = getattr(rule, "enabled", True)
171
+ status = "" if enabled else " [disabled]"
172
+ lines.append(f" [{sev:>8}] {qid}{status}")
173
+ if verbose and desc:
174
+ lines.append(f" {desc}")
175
+ lines.append("")
176
+
177
+ return "\n".join(lines)
178
+
179
+
180
+ def format_packs_list(packs: list[dict]) -> str:
181
+ """Format language pack info for list_packs MCP tool."""
182
+ if not packs:
183
+ return "No language packs installed."
184
+
185
+ lines = [f"## Language Packs: {len(packs)} installed\n"]
186
+ for pack in packs:
187
+ name = pack.get("name", "?")
188
+ langs = ", ".join(pack.get("languages", []))
189
+ rules_count = pack.get("rules_count", 0)
190
+ desc = pack.get("description", "")
191
+ lines.append(f"### {name}")
192
+ lines.append(f" Languages: {langs}")
193
+ lines.append(f" Rules: {rules_count}")
194
+ if desc:
195
+ lines.append(f" {desc}")
196
+ lines.append("")
197
+
198
+ return "\n".join(lines)
@@ -0,0 +1,242 @@
1
+ """Rule hygiene — auto-prune dead/noisy rules and report confidence drift.
2
+
3
+ Reads persistent state from :class:`FeedbackStore` (counts, calibrated
4
+ confidence) and the in-memory :class:`RuleRegistry` (current rule set
5
+ plus declared confidence) to produce a hygiene report and, optionally,
6
+ apply disable decisions.
7
+
8
+ Three categories:
9
+
10
+ - **Dead**: rule has been applied across at least ``min_scans`` separate
11
+ analyze sessions but produced zero matches.
12
+ - **Noisy**: rule has at least ``min_samples`` TP/FP feedback observations
13
+ with a false-positive rate above ``fp_threshold``.
14
+ - **Drift**: calibrated confidence diverges from the rule's declared
15
+ confidence by more than ``drift_threshold``. Reported only — never
16
+ auto-disabled (operators may want to recalibrate the YAML instead).
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from dataclasses import dataclass, field
22
+ from typing import TYPE_CHECKING
23
+
24
+ if TYPE_CHECKING:
25
+ from attocode_intel.rules.profiling import FeedbackStore
26
+ from attocode_intel.rules.registry import RuleRegistry
27
+
28
+
29
+ # Defaults match the Phase 1 roadmap entry for auto-pruning.
30
+ DEFAULT_MIN_SCANS = 10
31
+ DEFAULT_MIN_SAMPLES = 10
32
+ DEFAULT_FP_THRESHOLD = 0.5
33
+ DEFAULT_DRIFT_THRESHOLD = 0.2
34
+
35
+
36
+ @dataclass(slots=True, frozen=True)
37
+ class HygieneEntry:
38
+ """One hygiene observation about a single rule."""
39
+
40
+ rule_id: str
41
+ reason: str # "dead" | "noisy" | "drift"
42
+ detail: str # human-readable summary (counts, rates, deltas)
43
+ scans: int = 0
44
+ matches: int = 0
45
+ tp: int = 0
46
+ fp: int = 0
47
+ declared_confidence: float = 0.0
48
+ calibrated_confidence: float | None = None
49
+
50
+
51
+ @dataclass(slots=True)
52
+ class HygieneReport:
53
+ """Aggregated hygiene findings across the registry."""
54
+
55
+ dead: list[HygieneEntry] = field(default_factory=list)
56
+ noisy: list[HygieneEntry] = field(default_factory=list)
57
+ drift: list[HygieneEntry] = field(default_factory=list)
58
+ rules_examined: int = 0
59
+
60
+ @property
61
+ def total_actionable(self) -> int:
62
+ """Dead + noisy — entries that ``apply_hygiene`` will disable."""
63
+ return len(self.dead) + len(self.noisy)
64
+
65
+
66
+ def compute_hygiene(
67
+ store: FeedbackStore,
68
+ registry: RuleRegistry,
69
+ *,
70
+ min_scans: int = DEFAULT_MIN_SCANS,
71
+ min_samples: int = DEFAULT_MIN_SAMPLES,
72
+ fp_threshold: float = DEFAULT_FP_THRESHOLD,
73
+ drift_threshold: float = DEFAULT_DRIFT_THRESHOLD,
74
+ ) -> HygieneReport:
75
+ """Compute dead/noisy/drift entries for all registered rules."""
76
+ report = HygieneReport()
77
+ for rule in registry.all_rules(enabled_only=False):
78
+ qid = rule.qualified_id
79
+ report.rules_examined += 1
80
+
81
+ scans = store.get_scan_count(qid)
82
+ matches = store.get_match_count(qid)
83
+ feedback = store.get_feedback(qid)
84
+ tp, fp = feedback["tp"], feedback["fp"]
85
+ calibrated = store.get_calibrated_confidence(qid)
86
+
87
+ # Dead: applied enough times, never fired.
88
+ if matches == 0 and scans >= min_scans:
89
+ report.dead.append(
90
+ HygieneEntry(
91
+ rule_id=qid,
92
+ reason="dead",
93
+ detail=f"{scans} scans, 0 matches",
94
+ scans=scans,
95
+ matches=matches,
96
+ tp=tp,
97
+ fp=fp,
98
+ declared_confidence=rule.confidence,
99
+ calibrated_confidence=calibrated,
100
+ )
101
+ )
102
+ continue
103
+
104
+ # Noisy: enough feedback samples and FP rate too high.
105
+ total_samples = tp + fp
106
+ if total_samples >= min_samples:
107
+ fp_rate = fp / total_samples
108
+ if fp_rate > fp_threshold:
109
+ report.noisy.append(
110
+ HygieneEntry(
111
+ rule_id=qid,
112
+ reason="noisy",
113
+ detail=f"{fp}/{total_samples} FP ({fp_rate:.0%})",
114
+ scans=scans,
115
+ matches=matches,
116
+ tp=tp,
117
+ fp=fp,
118
+ declared_confidence=rule.confidence,
119
+ calibrated_confidence=calibrated,
120
+ )
121
+ )
122
+ continue
123
+
124
+ # Drift: calibrated confidence diverges from declared.
125
+ if calibrated is not None:
126
+ delta = calibrated - rule.confidence
127
+ if abs(delta) >= drift_threshold:
128
+ direction = "+" if delta > 0 else "-"
129
+ report.drift.append(
130
+ HygieneEntry(
131
+ rule_id=qid,
132
+ reason="drift",
133
+ detail=(
134
+ f"declared {rule.confidence:.2f} → calibrated "
135
+ f"{calibrated:.2f} ({direction}{abs(delta):.2f})"
136
+ ),
137
+ scans=scans,
138
+ matches=matches,
139
+ tp=tp,
140
+ fp=fp,
141
+ declared_confidence=rule.confidence,
142
+ calibrated_confidence=calibrated,
143
+ )
144
+ )
145
+
146
+ return report
147
+
148
+
149
+ def apply_hygiene(
150
+ report: HygieneReport,
151
+ registry: RuleRegistry,
152
+ store: FeedbackStore,
153
+ ) -> int:
154
+ """Disable dead and noisy rules; persist the decision in ``store``.
155
+
156
+ Drift entries are not auto-disabled — they're informational. Returns
157
+ the number of rules disabled.
158
+ """
159
+ disabled = 0
160
+ for entry in [*report.dead, *report.noisy]:
161
+ rule = registry.get(entry.rule_id)
162
+ if rule is None:
163
+ continue
164
+ rule.enabled = False
165
+ rule.disabled_reason = entry.reason
166
+ store.set_disabled(entry.rule_id, entry.reason)
167
+ disabled += 1
168
+ return disabled
169
+
170
+
171
+ def apply_persistent_disable(
172
+ registry: RuleRegistry,
173
+ store: FeedbackStore,
174
+ ) -> int:
175
+ """Re-apply previously persisted hygiene-disable decisions on startup.
176
+
177
+ Called by ``_get_registry`` after rules load so dead/noisy rules from
178
+ prior sessions stay disabled. Returns the number of rules touched.
179
+ """
180
+ touched = 0
181
+ for rule in registry.all_rules(enabled_only=False):
182
+ qid = rule.qualified_id
183
+ if store.is_disabled(qid):
184
+ rule.enabled = False
185
+ rule.disabled_reason = store.get_disabled_reason(qid) or "disabled"
186
+ touched += 1
187
+ return touched
188
+
189
+
190
+ def format_hygiene_report(report: HygieneReport) -> str:
191
+ """Render a hygiene report as agent-friendly markdown."""
192
+ if not (report.dead or report.noisy or report.drift):
193
+ return (
194
+ f"# Rule Hygiene\n\nNo issues across {report.rules_examined} rules. "
195
+ "Run `analyze` and provide `rule_feedback` to gather more signal."
196
+ )
197
+
198
+ out: list[str] = [
199
+ "# Rule Hygiene",
200
+ "",
201
+ f"Examined {report.rules_examined} rules. "
202
+ f"Dead: {len(report.dead)}, Noisy: {len(report.noisy)}, "
203
+ f"Drift: {len(report.drift)}.",
204
+ ]
205
+
206
+ if report.dead:
207
+ out.append("\n## Dead rules — never matched\n")
208
+ out.append("| Rule | Scans | Matches | TP | FP |")
209
+ out.append("|------|-------|---------|----|----|")
210
+ for e in report.dead:
211
+ out.append(
212
+ f"| `{e.rule_id}` | {e.scans} | {e.matches} | {e.tp} | {e.fp} |"
213
+ )
214
+
215
+ if report.noisy:
216
+ out.append("\n## Noisy rules — high false-positive rate\n")
217
+ out.append("| Rule | TP | FP | Rate |")
218
+ out.append("|------|----|----|------|")
219
+ for e in report.noisy:
220
+ total = e.tp + e.fp
221
+ rate = e.fp / total if total else 0.0
222
+ out.append(f"| `{e.rule_id}` | {e.tp} | {e.fp} | {rate:.0%} |")
223
+
224
+ if report.drift:
225
+ out.append("\n## Confidence drift — calibrated diverges from declared\n")
226
+ out.append("| Rule | Declared | Calibrated | Δ |")
227
+ out.append("|------|----------|------------|---|")
228
+ for e in report.drift:
229
+ cal = e.calibrated_confidence or 0.0
230
+ delta = cal - e.declared_confidence
231
+ sign = "+" if delta >= 0 else "−"
232
+ out.append(
233
+ f"| `{e.rule_id}` | {e.declared_confidence:.2f} | "
234
+ f"{cal:.2f} | {sign}{abs(delta):.2f} |"
235
+ )
236
+
237
+ if report.total_actionable:
238
+ out.append(
239
+ f"\n_Run `rule_hygiene(apply=True)` to disable "
240
+ f"{report.total_actionable} dead/noisy rule(s)._"
241
+ )
242
+ return "\n".join(out)
@@ -0,0 +1 @@
1
+ """Rule importers — convert rules from other tool formats to attocode YAML."""