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,326 @@
1
+ """Rule testing framework with inline annotations.
2
+
3
+ Verifies that rules produce the correct findings against annotated
4
+ source files. Three annotation types are supported:
5
+
6
+ ``# expect: rule-id`` — rule MUST fire on this line (TP assertion)
7
+ ``# ok: rule-id`` — rule must NOT fire on this line (FP guard)
8
+ ``# todoruleid: rule-id`` — rule SHOULD fire but is known-missing (not fatal)
9
+
10
+ Non-Python comments (``//``) are also supported.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import re
16
+ from dataclasses import dataclass, field
17
+ from pathlib import Path
18
+
19
+ from attocode_intel.rules.executor import execute_rules
20
+ from attocode_intel.rules.filters.pipeline import run_pipeline
21
+ from attocode_intel.rules.model import EnrichedFinding, UnifiedRule
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # Annotation parsing
25
+ # ---------------------------------------------------------------------------
26
+
27
+ # Matches: # expect: rule-id, // expect: rule-id, # ok: rule-id, etc.
28
+ # Also accepts the semgrep-rules ``ruleid:`` marker as an alias for ``expect:``
29
+ # so test fixtures imported from semgrep work without preprocessing.
30
+ _ANNOTATION_RE = re.compile(
31
+ r"(?:#|//)\s*(expect|ok|todoruleid|ruleid):\s*([\w./-]+)"
32
+ )
33
+
34
+
35
+ @dataclass(slots=True, frozen=True)
36
+ class Expectation:
37
+ """A single test annotation from a source file."""
38
+
39
+ line: int
40
+ kind: str # "expect" | "ok" | "todoruleid"
41
+ rule_id: str
42
+
43
+
44
+ def parse_annotations(file_path: str) -> list[Expectation]:
45
+ """Parse all test annotations from a source file."""
46
+ expectations: list[Expectation] = []
47
+ try:
48
+ with open(file_path, encoding="utf-8") as f:
49
+ for i, line in enumerate(f, 1):
50
+ for m in _ANNOTATION_RE.finditer(line):
51
+ kind = m.group(1)
52
+ # semgrep "ruleid" is treated as our "expect"
53
+ if kind == "ruleid":
54
+ kind = "expect"
55
+ expectations.append(Expectation(
56
+ line=i,
57
+ kind=kind,
58
+ rule_id=m.group(2),
59
+ ))
60
+ except OSError:
61
+ pass # unreadable file — return empty
62
+ return expectations
63
+
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Test result model
67
+ # ---------------------------------------------------------------------------
68
+
69
+
70
+ @dataclass(slots=True)
71
+ class RuleTestFileResult:
72
+ """Result of testing rules against a single annotated file."""
73
+
74
+ file_path: str
75
+ passed: list[Expectation] = field(default_factory=list)
76
+ failed: list[tuple[Expectation, str]] = field(default_factory=list)
77
+ todoruleid_unexpected_passes: list[Expectation] = field(default_factory=list)
78
+ false_positives: list[tuple[EnrichedFinding, Expectation]] = field(default_factory=list)
79
+
80
+ @property
81
+ def ok(self) -> bool:
82
+ return not self.failed and not self.false_positives
83
+
84
+ @property
85
+ def total_assertions(self) -> int:
86
+ return len(self.passed) + len(self.failed) + len(self.false_positives) + len(self.todoruleid_unexpected_passes)
87
+
88
+
89
+ @dataclass(slots=True)
90
+ class RuleTestSuiteResult:
91
+ """Aggregated result across multiple test files."""
92
+
93
+ file_results: list[RuleTestFileResult] = field(default_factory=list)
94
+
95
+ @property
96
+ def ok(self) -> bool:
97
+ return all(r.ok for r in self.file_results)
98
+
99
+ @property
100
+ def total_passed(self) -> int:
101
+ return sum(len(r.passed) for r in self.file_results)
102
+
103
+ @property
104
+ def total_failed(self) -> int:
105
+ return sum(len(r.failed) for r in self.file_results)
106
+
107
+ @property
108
+ def total_false_positives(self) -> int:
109
+ return sum(len(r.false_positives) for r in self.file_results)
110
+
111
+ @property
112
+ def total_todoruleid_passes(self) -> int:
113
+ return sum(len(r.todoruleid_unexpected_passes) for r in self.file_results)
114
+
115
+
116
+ # ---------------------------------------------------------------------------
117
+ # Matching helpers
118
+ # ---------------------------------------------------------------------------
119
+
120
+
121
+ def _finding_matches_rule(finding_rule_id: str, expected_rule_id: str) -> bool:
122
+ """Check if a finding's rule_id matches an expected annotation.
123
+
124
+ Matching rules (in order):
125
+ 1. Exact match: ``"security/eval"`` == ``"security/eval"``
126
+ 2. Suffix match: ``"plugin:team/no-print"`` ends with ``"/no-print"``
127
+ """
128
+ if finding_rule_id == expected_rule_id:
129
+ return True
130
+ if finding_rule_id.endswith("/" + expected_rule_id):
131
+ return True
132
+ return False
133
+
134
+
135
+ # Public alias — the rule-bench harness reuses this matcher.
136
+ match_finding_to_expectation = _finding_matches_rule
137
+
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # Test runner
141
+ # ---------------------------------------------------------------------------
142
+
143
+
144
+ class RuleTestRunner:
145
+ """Run rule tests against annotated fixture files."""
146
+
147
+ def __init__(self, rules: list[UnifiedRule], *, project_dir: str = "") -> None:
148
+ self._rules = rules
149
+ self._project_dir = project_dir
150
+
151
+ def run_test_file(self, file_path: str) -> RuleTestFileResult:
152
+ """Run rules against a single annotated file and check expectations."""
153
+ result = RuleTestFileResult(file_path=file_path)
154
+ annotations = parse_annotations(file_path)
155
+
156
+ if not annotations:
157
+ return result
158
+
159
+ # Execute rules
160
+ findings = execute_rules(
161
+ [file_path], self._rules, project_dir=self._project_dir,
162
+ )
163
+ from attocode_intel.confidence.settings import disabled
164
+
165
+ with disabled():
166
+ findings = run_pipeline(findings, min_confidence=0.0)
167
+
168
+ # Build finding lookup: line -> list of rule_ids
169
+ finding_map: dict[int, list[str]] = {}
170
+ finding_objs: dict[tuple[int, str], EnrichedFinding] = {}
171
+ for f in findings:
172
+ finding_map.setdefault(f.line, []).append(f.rule_id)
173
+ finding_objs[(f.line, f.rule_id)] = f
174
+
175
+ for ann in annotations:
176
+ line_findings = finding_map.get(ann.line, [])
177
+ matched = any(
178
+ _finding_matches_rule(fid, ann.rule_id)
179
+ for fid in line_findings
180
+ )
181
+
182
+ if ann.kind == "expect":
183
+ if matched:
184
+ result.passed.append(ann)
185
+ else:
186
+ result.failed.append((
187
+ ann,
188
+ f"Expected '{ann.rule_id}' on line {ann.line} "
189
+ f"but got {line_findings or 'nothing'}",
190
+ ))
191
+
192
+ elif ann.kind == "ok":
193
+ if matched:
194
+ # Find the offending finding for the report
195
+ for fid in line_findings:
196
+ if _finding_matches_rule(fid, ann.rule_id):
197
+ obj = finding_objs.get((ann.line, fid))
198
+ if obj:
199
+ result.false_positives.append((obj, ann))
200
+ break
201
+ else:
202
+ result.passed.append(ann)
203
+
204
+ elif ann.kind == "todoruleid":
205
+ if matched:
206
+ result.todoruleid_unexpected_passes.append(ann)
207
+ # Not matching is expected — not a failure
208
+
209
+ return result
210
+
211
+ def run_test_suite(self, fixtures_dir: str) -> RuleTestSuiteResult:
212
+ """Run rules against all annotated files in a directory."""
213
+ suite = RuleTestSuiteResult()
214
+ fixtures_path = Path(fixtures_dir)
215
+ if not fixtures_path.is_dir():
216
+ return suite
217
+
218
+ for file_path in sorted(fixtures_path.rglob("*")):
219
+ if not file_path.is_file():
220
+ continue
221
+ if file_path.suffix not in {
222
+ ".py", ".go", ".ts", ".js", ".rs", ".java",
223
+ ".kt", ".rb", ".php", ".swift", ".cs", ".c",
224
+ ".cpp", ".lua", ".sh", ".scala", ".ex",
225
+ }:
226
+ continue
227
+ # Only test files that have annotations
228
+ content = file_path.read_text(encoding="utf-8", errors="replace")
229
+ if not _ANNOTATION_RE.search(content):
230
+ continue
231
+
232
+ result = self.run_test_file(str(file_path))
233
+ suite.file_results.append(result)
234
+
235
+ return suite
236
+
237
+
238
+ # ---------------------------------------------------------------------------
239
+ # Inline test_cases validation (YAML rule self-check)
240
+ # ---------------------------------------------------------------------------
241
+
242
+
243
+ def validate_inline_test_cases(
244
+ rule: UnifiedRule,
245
+ test_cases: list[dict],
246
+ ) -> list[str]:
247
+ """Validate a rule's inline test_cases at load time.
248
+
249
+ Each test case: ``{"code": "...", "should_match": true/false}``.
250
+ Returns list of error messages (empty = all passed).
251
+ """
252
+ errors: list[str] = []
253
+ if not rule.pattern:
254
+ return errors
255
+
256
+ for i, tc in enumerate(test_cases):
257
+ code = str(tc.get("code", ""))
258
+ should_match = bool(tc.get("should_match", True))
259
+ # Match per-line (consistent with executor behavior)
260
+ matched = any(rule.pattern.search(line) for line in code.splitlines()) if code else False
261
+
262
+ if should_match and not matched:
263
+ errors.append(
264
+ f"Rule '{rule.id}' test_case[{i}]: expected match on "
265
+ f"'{code[:60]}' but pattern did not match"
266
+ )
267
+ elif not should_match and matched:
268
+ errors.append(
269
+ f"Rule '{rule.id}' test_case[{i}]: expected no match on "
270
+ f"'{code[:60]}' but pattern matched"
271
+ )
272
+
273
+ return errors
274
+
275
+
276
+ # ---------------------------------------------------------------------------
277
+ # Report formatting
278
+ # ---------------------------------------------------------------------------
279
+
280
+
281
+ def format_test_report(suite: RuleTestSuiteResult) -> str:
282
+ """Format a test suite result as markdown."""
283
+ lines: list[str] = []
284
+
285
+ status = "PASS" if suite.ok else "FAIL"
286
+ lines.append(f"# Rule Test Report: {status}\n")
287
+ lines.append(
288
+ f"Files: {len(suite.file_results)} | "
289
+ f"Passed: {suite.total_passed} | "
290
+ f"Failed: {suite.total_failed} | "
291
+ f"False Positives: {suite.total_false_positives} | "
292
+ f"Todo Passes: {suite.total_todoruleid_passes}\n"
293
+ )
294
+
295
+ for fr in suite.file_results:
296
+ if fr.ok and not fr.todoruleid_unexpected_passes:
297
+ continue # Only show interesting results
298
+
299
+ rel = Path(fr.file_path).name
300
+ lines.append(f"## {rel}\n")
301
+
302
+ if fr.failed:
303
+ lines.append("### Missing Expected Findings\n")
304
+ for exp, reason in fr.failed:
305
+ lines.append(f"- Line {exp.line}: `{exp.rule_id}` — {reason}")
306
+ lines.append("")
307
+
308
+ if fr.false_positives:
309
+ lines.append("### False Positives (# ok: violations)\n")
310
+ for finding, ann in fr.false_positives:
311
+ lines.append(
312
+ f"- Line {ann.line}: `{ann.rule_id}` fired but was marked `# ok:` "
313
+ f"({finding.code_snippet[:60]})"
314
+ )
315
+ lines.append("")
316
+
317
+ if fr.todoruleid_unexpected_passes:
318
+ lines.append("### Unexpected Passes (todoruleid now matching!)\n")
319
+ for exp in fr.todoruleid_unexpected_passes:
320
+ lines.append(
321
+ f"- Line {exp.line}: `{exp.rule_id}` — "
322
+ f"previously known-missing, now matching! Consider upgrading to `# expect:`"
323
+ )
324
+ lines.append("")
325
+
326
+ return "\n".join(lines)