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,353 @@
1
+ """Distillation tools for the code-intel MCP server.
2
+
3
+ Provides a ``distill`` tool that compresses codebase information at
4
+ three fidelity levels: full (existing repo_map), signatures (public API
5
+ surface), and structure (file tree + import graph).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from collections import deque
12
+
13
+ from attocode_intel._shared import (
14
+ _get_ast_service,
15
+ _get_context_mgr,
16
+ _get_remote_service,
17
+ mcp,
18
+ )
19
+ from attocode_intel._internal.integrations.utilities.token_estimate import estimate_tokens
20
+
21
+ # ---------------------------------------------------------------------------
22
+ # Helpers
23
+ # ---------------------------------------------------------------------------
24
+
25
+
26
+ def _is_public_name(name: str) -> bool:
27
+ """Return True if *name* looks public (no leading underscore)."""
28
+ return not name.startswith("_")
29
+
30
+
31
+ def _extract_signatures(
32
+ ast_cache: dict,
33
+ rel_paths: list[str],
34
+ max_tokens: int,
35
+ ) -> str:
36
+ """Extract public API surface from AST cache for *rel_paths*.
37
+
38
+ Produces compact Python-like signatures for public functions, classes,
39
+ and their methods. Each function includes parameter names + types and
40
+ return type. Docstrings are reduced to first line only.
41
+
42
+ Uses ``estimate_tokens()`` for token estimation (same as ``repo_map``).
43
+ """
44
+ sections: list[str] = []
45
+ token_est = 0
46
+
47
+ for rel in rel_paths:
48
+ file_ast = ast_cache.get(rel)
49
+ if file_ast is None:
50
+ continue
51
+
52
+ file_lines: list[str] = [f"# {rel}"]
53
+ has_content = False
54
+
55
+ # Public top-level functions
56
+ for fn in file_ast.functions:
57
+ if fn.visibility != "public" and not _is_public_name(fn.name):
58
+ continue
59
+
60
+ sig = _format_function_sig(fn)
61
+ file_lines.append(sig)
62
+
63
+ # First-line docstring
64
+ doc_line = _first_line_docstring(fn.docstring)
65
+ if doc_line:
66
+ file_lines.append(f' """{doc_line}"""')
67
+
68
+ has_content = True
69
+
70
+ # Public classes
71
+ for cls in file_ast.classes:
72
+ if not _is_public_name(cls.name):
73
+ continue
74
+
75
+ bases = f"({', '.join(cls.bases)})" if cls.bases else ""
76
+ file_lines.append(f"class {cls.name}{bases}:")
77
+
78
+ # Class docstring
79
+ cls_doc = _first_line_docstring(cls.docstring)
80
+ if cls_doc:
81
+ file_lines.append(f' """{cls_doc}"""')
82
+
83
+ # Public methods
84
+ method_count = 0
85
+ for method in cls.methods:
86
+ if method.visibility != "public" and not _is_public_name(method.name):
87
+ continue
88
+
89
+ sig = _format_function_sig(method, indent=4)
90
+ file_lines.append(sig)
91
+
92
+ m_doc = _first_line_docstring(method.docstring)
93
+ if m_doc:
94
+ file_lines.append(f' """{m_doc}"""')
95
+
96
+ method_count += 1
97
+
98
+ if method_count == 0 and not cls_doc:
99
+ file_lines.append(" ...")
100
+
101
+ has_content = True
102
+
103
+ if not has_content:
104
+ continue
105
+
106
+ section_text = "\n".join(file_lines)
107
+ section_tokens = estimate_tokens(section_text)
108
+
109
+ if token_est + section_tokens > max_tokens and sections:
110
+ remaining = len(rel_paths) - len(sections)
111
+ if remaining > 0:
112
+ sections.append(f"\n# ... {remaining} more file(s) omitted (token budget)")
113
+ break
114
+
115
+ sections.append(section_text)
116
+ token_est += section_tokens
117
+
118
+ return "\n\n".join(sections)
119
+
120
+
121
+ def _format_function_sig(fn, indent: int = 0) -> str:
122
+ """Format a FunctionDef as a compact signature line."""
123
+ prefix = " " * indent
124
+ async_kw = "async " if fn.is_async else ""
125
+
126
+ # Build parameter list from detailed parameters if available
127
+ if fn.parameters:
128
+ params = []
129
+ for p in fn.parameters:
130
+ part = p.name
131
+ if p.type_annotation:
132
+ part += f": {p.type_annotation}"
133
+ if p.default_value:
134
+ part += f" = {p.default_value}"
135
+ if p.is_rest:
136
+ part = f"*{part}"
137
+ elif p.is_kwargs:
138
+ part = f"**{part}"
139
+ params.append(part)
140
+ param_str = ", ".join(params)
141
+ elif fn.params:
142
+ # Fallback to simple param names
143
+ param_str = ", ".join(fn.params)
144
+ else:
145
+ param_str = ""
146
+
147
+ ret = f" -> {fn.return_type}" if fn.return_type else ""
148
+ return f"{prefix}{async_kw}def {fn.name}({param_str}){ret}: ..."
149
+
150
+
151
+ def _first_line_docstring(docstring: str) -> str:
152
+ """Return the first non-empty line of a docstring, or ''."""
153
+ if not docstring:
154
+ return ""
155
+ for line in docstring.strip().splitlines():
156
+ stripped = line.strip().strip('"').strip("'").strip()
157
+ if stripped:
158
+ # Truncate very long first lines
159
+ if len(stripped) > 120:
160
+ return stripped[:117] + "..."
161
+ return stripped
162
+ return ""
163
+
164
+
165
+ def _build_structure(
166
+ files: list,
167
+ dep_graph,
168
+ rel_paths: list[str] | None,
169
+ max_tokens: int,
170
+ ) -> str:
171
+ """Build file tree + import graph adjacency list.
172
+
173
+ This is the maximum compression level (~90%+).
174
+ """
175
+ sections: list[str] = []
176
+
177
+ # File tree
178
+ tree_files = sorted(rel_paths) if rel_paths else sorted(fi.relative_path for fi in files)
179
+
180
+ tree_lines = ["# File tree"]
181
+ for path in tree_files:
182
+ tree_lines.append(f" {path}")
183
+ tree_text = "\n".join(tree_lines)
184
+ sections.append(tree_text)
185
+
186
+ # Import graph adjacency list
187
+ if dep_graph is not None:
188
+ graph_lines = ["# Import graph (file -> imports)"]
189
+ graph_set = set(rel_paths) if rel_paths else None
190
+ for src in sorted(dep_graph.forward.keys()):
191
+ if graph_set is not None and src not in graph_set:
192
+ continue
193
+ targets = dep_graph.forward.get(src, set())
194
+ if graph_set is not None:
195
+ targets = targets & graph_set
196
+ if targets:
197
+ target_list = ", ".join(sorted(targets))
198
+ graph_lines.append(f" {src} -> {target_list}")
199
+
200
+ if len(graph_lines) > 1:
201
+ graph_text = "\n".join(graph_lines)
202
+ sections.append(graph_text)
203
+
204
+ result = "\n\n".join(sections)
205
+
206
+ # Truncate if over budget
207
+ max_chars = int(max_tokens * 3.5)
208
+ if len(result) > max_chars:
209
+ result = result[:max_chars] + "\n# ... truncated (token budget)"
210
+
211
+ return result
212
+
213
+
214
+ def _expand_by_depth(
215
+ center_files: list[str],
216
+ dep_graph,
217
+ depth: int,
218
+ ) -> list[str]:
219
+ """BFS expand from center files through dependency graph up to *depth* hops."""
220
+ if dep_graph is None:
221
+ return center_files
222
+
223
+ visited: set[str] = set(center_files)
224
+ queue: deque[tuple[str, int]] = deque()
225
+ for f in center_files:
226
+ queue.append((f, 0))
227
+
228
+ while queue:
229
+ current, d = queue.popleft()
230
+ if d >= depth:
231
+ continue
232
+
233
+ # Forward: files this one imports
234
+ for dep in dep_graph.get_imports(current):
235
+ if dep not in visited:
236
+ visited.add(dep)
237
+ queue.append((dep, d + 1))
238
+
239
+ # Reverse: files that import this one
240
+ for dep in dep_graph.get_importers(current):
241
+ if dep not in visited:
242
+ visited.add(dep)
243
+ queue.append((dep, d + 1))
244
+
245
+ return sorted(visited)
246
+
247
+
248
+ # ---------------------------------------------------------------------------
249
+ # MCP Tool
250
+ # ---------------------------------------------------------------------------
251
+
252
+
253
+ @mcp.tool()
254
+ def distill(
255
+ files: list[str] | None = None,
256
+ depth: int = 1,
257
+ level: str = "signatures",
258
+ max_tokens: int = 4000,
259
+ ) -> str:
260
+ """Compress codebase information at varying fidelity levels.
261
+
262
+ Three distillation levels for different context budget needs:
263
+ - "full": Complete repo map with symbols (delegates to repo_map)
264
+ - "signatures": Public API surface — function/method signatures,
265
+ class definitions, type hints, first-line docstrings (~70% compression)
266
+ - "structure": File tree + import graph adjacency list only (~90%+ compression)
267
+
268
+ File selection:
269
+ - If files are specified, includes those files plus neighbors up to
270
+ `depth` hops via the dependency graph.
271
+ - If files is None, auto-selects the most important files (by PageRank
272
+ importance) that fit within the token budget.
273
+
274
+ Args:
275
+ files: Specific file paths to distill (relative to project root).
276
+ When None, auto-selects by importance.
277
+ depth: Number of dependency graph hops to expand from specified files
278
+ (default 1, max 3). Ignored when files is None.
279
+ level: Distillation level — "full", "signatures", or "structure".
280
+ max_tokens: Token budget for the output (default 4000).
281
+ """
282
+ remote = _get_remote_service()
283
+ if remote is not None:
284
+ return remote.distill(
285
+ files=files,
286
+ depth=depth,
287
+ level=level,
288
+ max_tokens=max_tokens,
289
+ )
290
+
291
+ # Level: full — delegate to existing repo_map
292
+ if level == "full":
293
+ from attocode_intel.tools.navigation_tools import repo_map as _repo_map
294
+
295
+ return _repo_map(include_symbols=True, max_tokens=max_tokens)
296
+
297
+ ctx = _get_context_mgr()
298
+ all_files = ctx._files
299
+
300
+ if not all_files:
301
+ return "No files discovered in this project."
302
+
303
+ svc = _get_ast_service()
304
+ ast_cache = svc._ast_cache
305
+ dep_graph = ctx.dependency_graph
306
+ depth = min(depth, 3)
307
+
308
+ # Determine which files to include
309
+ if files is not None:
310
+ # Normalize to relative paths
311
+ project_dir = ctx.root_dir
312
+ center_rels: list[str] = []
313
+ for f in files:
314
+ if os.path.isabs(f):
315
+ try:
316
+ rel = os.path.relpath(f, project_dir)
317
+ except ValueError:
318
+ rel = f
319
+ else:
320
+ rel = f
321
+ center_rels.append(rel)
322
+
323
+ # Expand by dependency depth
324
+ if depth > 0 and dep_graph is not None:
325
+ selected = _expand_by_depth(center_rels, dep_graph, depth)
326
+ else:
327
+ selected = sorted(set(center_rels))
328
+ else:
329
+ # Auto-select by importance (PageRank), fitting token budget
330
+ sorted_files = sorted(all_files, key=lambda fi: fi.importance, reverse=True)
331
+
332
+ # For signatures, estimate ~80 chars per file on average; for structure ~40 chars
333
+ avg_chars = 80 if level == "signatures" else 40
334
+ max_files_estimate = int(max_tokens * 3.5 / avg_chars)
335
+ selected = [fi.relative_path for fi in sorted_files[:max_files_estimate]]
336
+
337
+ if not selected:
338
+ return "No files matched the selection criteria."
339
+
340
+ # Generate output based on level
341
+ if level == "signatures":
342
+ output = _extract_signatures(ast_cache, selected, max_tokens)
343
+ elif level == "structure":
344
+ output = _build_structure(all_files, dep_graph, selected, max_tokens)
345
+ else:
346
+ return f"Unknown distillation level: '{level}'. Use 'full', 'signatures', or 'structure'."
347
+
348
+ if not output:
349
+ return "No content extracted (files may not have parseable ASTs)."
350
+
351
+ # Add summary footer
352
+ footer = f"\n\n({len(selected)} files, level={level}, ~{estimate_tokens(output)} tokens)"
353
+ return output + footer
@@ -0,0 +1,220 @@
1
+ """Frecency MCP tools for tracking and querying file access patterns.
2
+
3
+ Exposes frecency tracking to AI agents so they can learn which files
4
+ are commonly accessed and boost their relevance in search results.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import subprocess
11
+ from datetime import datetime
12
+ from pathlib import Path
13
+
14
+ from attocode_intel._shared import _get_frecency_tracker, _get_project_dir, mcp
15
+ from attocode_intel.tools.pin_tools import pin_stamped
16
+
17
+
18
+ def _get_git_status(file_path: str) -> tuple[bool, float | None]:
19
+ """Check if a file has uncommitted changes and get modification time.
20
+
21
+ Returns:
22
+ (is_modified, modified_timestamp or None)
23
+ """
24
+ project_dir = _get_project_dir()
25
+
26
+ try:
27
+ # Get git status for the specific file
28
+ result = subprocess.run(
29
+ ["git", "status", "--porcelain", "--", file_path],
30
+ cwd=project_dir,
31
+ capture_output=True,
32
+ text=True,
33
+ timeout=10,
34
+ )
35
+
36
+ is_modified = bool(result.stdout.strip())
37
+
38
+ # Get modification time
39
+ abs_path = Path(project_dir) / file_path
40
+ mtime = abs_path.stat().st_mtime if abs_path.exists() else None
41
+
42
+ return is_modified, mtime
43
+
44
+ except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
45
+ return False, None
46
+
47
+
48
+ @mcp.tool()
49
+ def track_file_access(path: str) -> str:
50
+ """Track that a file was accessed (opened/viewed).
51
+
52
+ Call this whenever an agent opens or reads a file. This builds up
53
+ access patterns that improve file search relevance over time.
54
+
55
+ Args:
56
+ path: Path to the file that was accessed (relative to project root
57
+ or absolute).
58
+
59
+ Returns:
60
+ Confirmation message with the file that was tracked.
61
+ """
62
+ tracker = _get_frecency_tracker()
63
+
64
+ # Resolve to absolute path if needed
65
+ project_dir = _get_project_dir()
66
+ if os.path.isabs(path):
67
+ try:
68
+ rel_path = os.path.relpath(path, project_dir)
69
+ except ValueError:
70
+ rel_path = path
71
+ else:
72
+ rel_path = path
73
+
74
+ tracker.track_access(rel_path)
75
+
76
+ return f"Tracked access to: {rel_path}"
77
+
78
+
79
+ @mcp.tool()
80
+ def get_file_frecency(path: str, ai_mode: bool = True) -> str:
81
+ """Get the frecency score for a file.
82
+
83
+ Frecency combines how often and how recently a file was accessed.
84
+ Higher scores indicate more frequently/recently accessed files.
85
+
86
+ Args:
87
+ path: Path to the file (relative to project root or absolute).
88
+ ai_mode: Use AI mode decay (faster, 3-day half-life vs 10-day).
89
+
90
+ Returns:
91
+ Frecency score and metadata for the file.
92
+ """
93
+ tracker = _get_frecency_tracker()
94
+
95
+ # Resolve path
96
+ project_dir = _get_project_dir()
97
+ if os.path.isabs(path):
98
+ try:
99
+ rel_path = os.path.relpath(path, project_dir)
100
+ except ValueError:
101
+ rel_path = path
102
+ else:
103
+ rel_path = path
104
+
105
+ # Get git status for modification bonus
106
+ is_modified, mtime = _get_git_status(rel_path)
107
+
108
+ result = tracker.get_score(
109
+ rel_path,
110
+ modified_time=mtime,
111
+ is_modified_git=is_modified,
112
+ ai_mode=ai_mode,
113
+ )
114
+
115
+ # Format output
116
+ mode_str = "AI" if result.is_ai_mode else "Human"
117
+ last_access_str = "never"
118
+ if result.last_access is not None:
119
+ dt = datetime.fromtimestamp(result.last_access)
120
+ last_access_str = dt.strftime("%Y-%m-%d %H:%M:%S")
121
+
122
+ lines = [
123
+ f"Frecency score for: {rel_path}",
124
+ f"Score: {result.score}",
125
+ f"Access count: {result.accesses}",
126
+ f"Last access: {last_access_str}",
127
+ f"Mode: {mode_str}",
128
+ ]
129
+
130
+ if is_modified:
131
+ lines.append("(Git modified bonus applied)")
132
+
133
+ return "\n".join(lines)
134
+
135
+
136
+ @mcp.tool()
137
+ @pin_stamped
138
+ def get_frecency_leaderboard(top_n: int = 20, ai_mode: bool = True) -> str:
139
+ """Get the top N most frequently accessed files.
140
+
141
+ Args:
142
+ top_n: Number of top files to return (default 20).
143
+ ai_mode: Use AI mode decay (faster, 3-day half-life vs 10-day).
144
+
145
+ Returns:
146
+ Leaderboard of most accessed files.
147
+ """
148
+ tracker = _get_frecency_tracker()
149
+ leaderboard = tracker.get_leaderboard(top_n=top_n, ai_mode=ai_mode)
150
+
151
+ if not leaderboard:
152
+ stats = tracker.get_stats()
153
+ return (
154
+ f"No files with positive frecency scores.\n"
155
+ f"Database has {stats['entries']} tracked files.\n"
156
+ f"Files need recent accesses to appear on the leaderboard."
157
+ )
158
+
159
+ mode_str = "AI (3-day decay)" if ai_mode else "Human (10-day decay)"
160
+ lines = [
161
+ f"Frecency Leaderboard (top {top_n}, {mode_str})",
162
+ "=" * 50,
163
+ ]
164
+
165
+ for rank, (path, result) in enumerate(leaderboard, 1):
166
+ last_str = "never"
167
+ if result.last_access is not None:
168
+ dt = datetime.fromtimestamp(result.last_access)
169
+ last_str = dt.strftime("%Y-%m-%d %H:%M")
170
+ lines.append(
171
+ f"{rank:3d}. [{result.score:4d}] {path} "
172
+ f"({result.accesses} accesses, last: {last_str})"
173
+ )
174
+
175
+ return "\n".join(lines)
176
+
177
+
178
+ @mcp.tool()
179
+ def clear_frecency(path: str | None = None) -> str:
180
+ """Clear frecency data.
181
+
182
+ Args:
183
+ path: Optional specific file path to clear. If not provided,
184
+ all frecency data is cleared.
185
+
186
+ Returns:
187
+ Confirmation message.
188
+ """
189
+ tracker = _get_frecency_tracker()
190
+
191
+ if path is None:
192
+ tracker.clear()
193
+ return "All frecency data cleared."
194
+ else:
195
+ count = tracker.clear(path)
196
+ if count > 0:
197
+ return f"Cleared frecency data for: {path}"
198
+ else:
199
+ return f"No frecency data found for: {path}"
200
+
201
+
202
+ @mcp.tool()
203
+ def get_frecency_stats() -> str:
204
+ """Get overall frecency database statistics.
205
+
206
+ Returns:
207
+ Statistics about the frecency database.
208
+ """
209
+ tracker = _get_frecency_tracker()
210
+ stats = tracker.get_stats()
211
+
212
+ lines = [
213
+ "Frecency Statistics",
214
+ "=" * 40,
215
+ f"Tracked files: {stats['entries']}",
216
+ f"Mode: {'AI (3-day decay)' if stats['ai_mode'] else 'Human (10-day decay)'}",
217
+ f"Database path: {stats.get('db_path', 'unknown')}",
218
+ ]
219
+
220
+ return "\n".join(lines)