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,185 @@
1
+ """Standalone CLI; importing it does not load the agent or analysis engines."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import json
8
+ import os
9
+ import sys
10
+ import threading
11
+ import time
12
+
13
+ COMMANDS = """commands:
14
+ init Add the server to Codex, Claude Code or Cursor (--client)
15
+ doctor Start the configured server and check that it answers
16
+ install <target> Add the server to another client, for example vscode or zed
17
+ share Send a learning to a team server
18
+ query <text> Search the code
19
+ symbols <file> List the symbols of a file, or find one with --search <name>
20
+ impact <file>... Show the files that a change to these files can affect
21
+ hotspots Show the files with the most risk or complexity
22
+ deps <file> Show what a file imports and what imports it
23
+ commands Show all commands, with maintenance commands and targets
24
+ """
25
+
26
+
27
+ def exit_when_client_exits(interval: float = 5.0) -> None:
28
+ """Stop a stdio server when the client process that started it is gone.
29
+
30
+ At end of input the MCP SDK still waits for running tool calls. A call
31
+ that never ended kept an orphaned server alive, at full CPU, for days.
32
+ SQLite indexes stay consistent after an abrupt exit. The watchdog does not
33
+ write a message: stderr usually goes to the closed client, and a write
34
+ error would stop the thread before the exit.
35
+ """
36
+ parent = os.getppid()
37
+
38
+ def watch() -> None:
39
+ while os.getppid() == parent:
40
+ time.sleep(interval)
41
+ os._exit(0)
42
+
43
+ threading.Thread(target=watch, name="client-watchdog", daemon=True).start()
44
+
45
+
46
+ def main(argv=None):
47
+ args = list(sys.argv[1:] if argv is None else argv)
48
+ if args and args[0] == "share":
49
+ from attocode_intel.sharing import share
50
+
51
+ share(args[1:])
52
+ return
53
+ if args and args[0] in {"init", "doctor"}:
54
+ command = args.pop(0)
55
+ parser = argparse.ArgumentParser(prog=f"attocode-code-intel {command}")
56
+ parser.add_argument("--client", choices=["codex", "claude", "cursor", "all"], default="all")
57
+ parser.add_argument("--project", default=".")
58
+ parser.add_argument("--global", dest="global_scope", action="store_true")
59
+ parser.add_argument("--profile", choices=["daily", "full"], default="daily")
60
+ parser.add_argument("--dry-run", action="store_true")
61
+ parser.add_argument("--remove", action="store_true")
62
+ parser.add_argument("--server", default="")
63
+ parser.add_argument("--repo", default="")
64
+ parser.add_argument(
65
+ "--remote", action="store_true", help="Doctor/remove: select the remote integration"
66
+ )
67
+ parser.add_argument("--token-env", default="ATTOCODE_API_KEY")
68
+ opts = parser.parse_args(args)
69
+ from attocode_intel.onboarding import configure_client, diagnose
70
+
71
+ clients = ["codex", "claude", "cursor"] if opts.client == "all" else [opts.client]
72
+ failed, ready = False, 0
73
+ for client in clients:
74
+ try:
75
+ if command == "init":
76
+ result = configure_client(
77
+ client,
78
+ opts.project,
79
+ global_scope=opts.global_scope,
80
+ profile=opts.profile,
81
+ dry_run=opts.dry_run,
82
+ remove=opts.remove,
83
+ server=opts.server,
84
+ token_env=opts.token_env,
85
+ repo=opts.repo or ("remote" if opts.remote else ""),
86
+ )
87
+ else:
88
+ result = asyncio.run(
89
+ diagnose(
90
+ client,
91
+ opts.project,
92
+ global_scope=opts.global_scope,
93
+ remote=bool(opts.remote or opts.server or opts.repo),
94
+ )
95
+ )
96
+ print(json.dumps(result, indent=2))
97
+ ready += result.get("status") != "not configured"
98
+ except Exception as exc:
99
+ # A timeout has no message of its own.
100
+ reason = "the server did not answer in 30 s" if isinstance(exc, TimeoutError) else exc
101
+ print(f"{client}: {reason}", file=sys.stderr)
102
+ failed = True
103
+ # With --client all, a client without configuration is not a failure, but one client must be ready.
104
+ if failed or not ready:
105
+ raise SystemExit(1)
106
+ return
107
+ if args and args[0] not in {"serve", "help"} and not args[0].startswith("-"):
108
+ from attocode_intel.cli import dispatch_code_intel
109
+
110
+ dispatch_code_intel(args)
111
+ return
112
+ if args and args[0] == "serve":
113
+ args.pop(0)
114
+ if args == ["help"]:
115
+ args = ["--help"]
116
+ parser = argparse.ArgumentParser(
117
+ prog="attocode-code-intel",
118
+ description="Code search and navigation for coding agents and for people. "
119
+ "Without a command, it runs the MCP server.",
120
+ epilog=COMMANDS,
121
+ formatter_class=argparse.RawDescriptionHelpFormatter,
122
+ )
123
+ parser.add_argument("--project", default=os.environ.get("ATTOCODE_PROJECT_DIR", ""))
124
+ parser.add_argument(
125
+ "--profile", choices=["daily", "full"], default="daily",
126
+ help="daily: a short tool list for agents (default); full: all tools",
127
+ )
128
+ parser.add_argument(
129
+ "--transport", choices=["stdio", "sse", "http", "streamable-http"], default="stdio"
130
+ )
131
+ parser.add_argument("--host", default="127.0.0.1")
132
+ parser.add_argument("--port", type=int, default=8080)
133
+ parser.add_argument(
134
+ "--local-only", action="store_true", help="Accepted for old configurations; it has no effect"
135
+ )
136
+ parser.add_argument("--no-watch", action="store_true")
137
+ parser.add_argument("--watch-debounce", type=int, default=500)
138
+ from attocode_intel import __version__
139
+
140
+ parser.add_argument("--version", action="version", version=f"attocode-code-intel {__version__}")
141
+ opts = parser.parse_args(args)
142
+ if opts.transport in {"http", "streamable-http"}:
143
+ from attocode_intel.cli import _serve_http
144
+
145
+ os.environ["ATTOCODE_MCP_PROFILE"] = opts.profile
146
+ os.environ["ATTOCODE_INTEL_WATCH"] = "0" if opts.no_watch else "1"
147
+ os.environ["ATTOCODE_INTEL_WATCH_DEBOUNCE"] = str(opts.watch_debounce)
148
+ _serve_http(opts.project, host=opts.host, port=opts.port, debug=False)
149
+ elif opts.transport == "sse":
150
+ from attocode_intel.config import is_loopback
151
+
152
+ if not is_loopback(opts.host):
153
+ # SSE has no sign-in, so any computer that reaches the port could read the index.
154
+ raise SystemExit("Error: SSE serves only this computer. For other hosts, use "
155
+ "--transport http with ATTOCODE_API_KEY.")
156
+ # ponytail: SSE runs the older server, which lists all tools. Use stdio or http for profiles.
157
+ from attocode_intel.server import mcp
158
+
159
+ mcp.settings.host, mcp.settings.port = opts.host, opts.port
160
+ if opts.project:
161
+ os.environ["ATTOCODE_PROJECT_DIR"] = opts.project
162
+ mcp.run(transport="sse")
163
+ else:
164
+ asyncio.run(_stdio(opts))
165
+
166
+
167
+ async def _stdio(opts):
168
+ from mcp.server.stdio import stdio_server
169
+
170
+ from attocode_intel.gateway import OperationGateway, create_mcp_server
171
+
172
+ gateway = OperationGateway(
173
+ opts.project, opts.profile, watch=not opts.no_watch, watch_debounce=opts.watch_debounce
174
+ )
175
+ server = create_mcp_server(gateway)
176
+ exit_when_client_exits()
177
+ try:
178
+ async with stdio_server() as (read, write):
179
+ await server.run(read, write, server.create_initialization_options())
180
+ finally:
181
+ await gateway.close()
182
+
183
+
184
+ if __name__ == "__main__":
185
+ main()
@@ -0,0 +1,130 @@
1
+ """Deterministic lexical evidence selection; excerpts are never stitched together."""
2
+ from __future__ import annotations
3
+
4
+ import io
5
+ import re
6
+ import tokenize
7
+ from collections import Counter
8
+
9
+ STOP_WORDS = frozenset({"a", "an", "and", "are", "as", "at", "be", "by", "code", "def", "do", "does", "for", "from", "function", "get", "how", "in", "is", "it", "method", "of", "on", "or", "return", "self", "test", "tests", "the", "this", "to", "use", "value", "with"})
10
+ _EXCLUSION = re.compile(r"\b(?:without|excluding|exclude|except|avoid)\b", re.IGNORECASE)
11
+ _EXCLUSION_END = re.compile(r"[,;.!?]|\b(?:but|instead|rather|while)\b", re.IGNORECASE)
12
+ _EXCLUSION_MODIFIERS = frozenset({"unrelated", "irrelevant", "unwanted", "other"})
13
+
14
+
15
+ def terms(text, fold=True):
16
+ """Query or evidence words; ``fold`` cuts a plural "s", so both sides must use the same rule."""
17
+ text = re.sub(r"([a-z0-9])([A-Z])", r"\1 \2", text)
18
+ words = re.findall(r"[a-zA-Z][a-zA-Z0-9]*", text.lower())
19
+ return {word[:-1] if fold and word.endswith("s") and not word.endswith("ss") and len(word) > 4 else word
20
+ for word in words if len(word) > 2 and word not in STOP_WORDS}
21
+
22
+
23
+ def task_terms(text: str, fold: bool = True) -> tuple[set[str], set[str]]:
24
+ """Separate explicit exclusions from positive task terms.
25
+
26
+ The negative set is a *ranking hint*, never a hard filter. Deliberately
27
+ recognize only explicit exclusion words: ordinary negation such as
28
+ ``not found`` often describes the bug rather than irrelevant code.
29
+ """
30
+ positive_parts: list[str] = []
31
+ negative_parts: list[str] = []
32
+ remaining = text
33
+ while match := _EXCLUSION.search(remaining):
34
+ positive_parts.append(remaining[:match.start()])
35
+ tail = remaining[match.end():]
36
+ end = _EXCLUSION_END.search(tail)
37
+ if end is None:
38
+ negative_parts.append(tail)
39
+ remaining = ""
40
+ break
41
+ negative_parts.append(tail[:end.start()])
42
+ remaining = tail[end.end():]
43
+ positive_parts.append(remaining)
44
+ positive = terms(" ".join(positive_parts), fold) - _EXCLUSION_MODIFIERS
45
+ negative = terms(" ".join(negative_parts), fold) - _EXCLUSION_MODIFIERS
46
+ return positive, negative
47
+
48
+
49
+ def file_excerpt(lines: list[str], path: str, query: str) -> str:
50
+ """Query-focused file evidence for a shortlist decision.
51
+
52
+ The product reranker and the eval trials show a model this same text. It
53
+ has up to two numbered windows around the lines with the most task terms.
54
+ """
55
+ if not lines:
56
+ return f"File: {path}\n(empty file)"
57
+ positive, _ = task_terms(query)
58
+ centers: list[int] = []
59
+ scored = sorted(((len(terms(line) & positive), -index, index)
60
+ for index, line in enumerate(lines)), reverse=True)
61
+ for overlap, _order, index in scored:
62
+ if overlap == 0 and centers:
63
+ break
64
+ if all(abs(index - old) > 24 for old in centers):
65
+ centers.append(index)
66
+ if len(centers) == 2:
67
+ break
68
+ windows = []
69
+ for center in sorted(centers or [0]):
70
+ start, end = max(0, center - 8), min(len(lines), center + 9)
71
+ windows.append("\n".join(f"{number + 1}: {lines[number]}"
72
+ for number in range(start, end)))
73
+ return (f"File: {path}\n" + "\n...\n".join(windows))[:1350]
74
+
75
+
76
+ def source_excerpts(source, definition, task_hint, *, preview_end=0):
77
+ """Rank code identifiers, favor branches/assignments, and return disjoint ranges.
78
+
79
+ This is a lexical aid, not semantic relevance or complete control-flow coverage.
80
+ Python strings/comments do not become evidence just by repeating query words.
81
+ """
82
+ from attocode_intel.structural_evidence import structural_excerpts
83
+ structured = structural_excerpts(source, definition, task_hint, preview_end)
84
+ if structured is not None:
85
+ return structured
86
+ query, _ = task_terms(task_hint)
87
+ if not query:
88
+ return []
89
+ first, last = definition.start_line, min(definition.end_line, len(source))
90
+ identifiers = {}
91
+ if definition.file_path.endswith(".py"):
92
+ try:
93
+ for token in tokenize.generate_tokens(io.StringIO("\n".join(source)).readline):
94
+ if token.type == tokenize.NAME and first <= token.start[0] <= last:
95
+ identifiers.setdefault(token.start[0], []).append(token.string)
96
+ except (tokenize.TokenError, IndentationError, SyntaxError):
97
+ pass # Only retain tokens actually read; do not invent coverage.
98
+ else:
99
+ for number in range(first, last + 1):
100
+ line = source[number - 1].strip()
101
+ if line.startswith(("#", "//", "/*", "*")):
102
+ continue
103
+ code = re.sub(r"(['\"]).*?\1", "", line).split("//", 1)[0]
104
+ identifiers[number] = re.findall(r"[A-Za-z_][A-Za-z_0-9]*", code)
105
+ candidates = []
106
+ for number, names in identifiers.items():
107
+ if number <= preview_end:
108
+ continue # The caller already supplies this source verbatim.
109
+ hits = Counter(term for name in names for term in terms(name) if term in query)
110
+ if not hits:
111
+ continue
112
+ code = source[number - 1].strip()
113
+ # Guards and stores often express behavior more directly than annotations.
114
+ action = bool(re.match(r"(?:if|elif|else|return|case|throw|raise)\b", code) or
115
+ (not code.endswith(",") and re.search(r"(?<![=!<>])=(?!=)", code)))
116
+ # Multiple participating identifiers help distinguish access from simple
117
+ # initialization, but repetition is capped so annotations cannot dominate.
118
+ score = sum(min(count, 2) for count in hits.values()) + 2 * int(action)
119
+ candidates.append((number, score, set(hits)))
120
+ selected, covered = [], set()
121
+ while candidates and len(selected) < 3:
122
+ number, _, hits = max(candidates, key=lambda row: (row[1] + len(row[2] - covered), -row[0]))
123
+ start, end = max(first, preview_end + 1, number - 1), min(last, number + 3)
124
+ selected.append({"file_path": definition.file_path, "start_line": start, "end_line": end,
125
+ "text": "\n".join(source[start - 1:end]), "matched_terms": sorted(hits), "context_ranges": [],
126
+ "context_incomplete": True, "context_relation": "structural context unavailable"})
127
+ covered.update(hits)
128
+ candidates = [row for row in candidates if min(last, row[0] + 3) < start or max(first, row[0] - 1) > end]
129
+ # Keep relevance ordering, so output budgeting drops lower ranked whole excerpts.
130
+ return selected
@@ -0,0 +1,154 @@
1
+ """Incremental invalidation based on filesystem identity, including import-only edits."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import os
7
+ import threading
8
+ from pathlib import Path
9
+
10
+ from attocode_intel._internal.integrations.utilities.ignore import IgnoreManager
11
+
12
+
13
+ class FreshnessTracker:
14
+ def __init__(self, root: str):
15
+ self.root = root
16
+ self.manifest = None
17
+ self.generation = 0
18
+ self._ignore = None
19
+ self._ignore_identity = None
20
+ self._eligibility = {}
21
+
22
+ def scan_manifest(self):
23
+ """Stat every eligible file, reusing only path-filter decisions.
24
+
25
+ No time window or watcher delivery is required for edit detection.
26
+ DirEntry avoids allocating/resolving a Path for each file on each call.
27
+ """
28
+ from attocode_intel._internal.integrations.context.codebase_context import (
29
+ SKIP_EXTENSIONS,
30
+ SKIP_FILENAMES,
31
+ )
32
+
33
+ try:
34
+ stat = os.stat(os.path.join(self.root, ".gitignore"))
35
+ ignore_identity = (stat.st_mtime_ns, stat.st_ctime_ns, stat.st_size)
36
+ except OSError:
37
+ ignore_identity = None
38
+ if self._ignore is None or ignore_identity != self._ignore_identity:
39
+ self._ignore = IgnoreManager(self.root)
40
+ self._ignore_identity = ignore_identity
41
+ self._eligibility.clear()
42
+ ignore = self._ignore
43
+ manifest = {}
44
+ eligibility = {}
45
+ pending = [(self.root, "")]
46
+ while pending:
47
+ directory, prefix = pending.pop()
48
+ try:
49
+ with os.scandir(directory) as entries:
50
+ for entry in entries:
51
+ name = entry.name
52
+ if name.startswith("."):
53
+ continue
54
+ rel = prefix + name
55
+ try:
56
+ is_dir = entry.is_dir()
57
+ key = rel + "/" if is_dir else rel
58
+ allowed = self._eligibility.get(key)
59
+ if allowed is None:
60
+ allowed = (is_dir or (
61
+ name not in SKIP_FILENAMES
62
+ and os.path.splitext(name)[1].lower() not in SKIP_EXTENSIONS
63
+ )) and not ignore.is_ignored(key)
64
+ eligibility[key] = allowed
65
+ if not allowed:
66
+ continue
67
+ if is_dir:
68
+ if not entry.is_symlink():
69
+ pending.append((entry.path, key))
70
+ else:
71
+ stat = entry.stat()
72
+ if stat.st_size <= 1_000_000:
73
+ manifest[rel] = (stat.st_mtime_ns, stat.st_ctime_ns, stat.st_size)
74
+ except OSError:
75
+ continue
76
+ except OSError:
77
+ continue
78
+ self._eligibility = eligibility
79
+ return manifest
80
+
81
+ def refresh(self, service):
82
+ manifest = self.scan_manifest()
83
+ previous = self.manifest
84
+ if previous is None or service._ast_service is None:
85
+ self.manifest = manifest
86
+ return
87
+ changed = {
88
+ p for p in previous.keys() | manifest.keys() if previous.get(p) != manifest.get(p)
89
+ }
90
+ if not changed:
91
+ self.manifest = manifest
92
+ return
93
+ self.generation += 1
94
+ from attocode_intel.request_context import current_request
95
+ request = current_request.get()
96
+ if request and (precision := request.stores.get("precision")):
97
+ precision.invalidate(changed)
98
+ if request:
99
+ request.stores.pop("reference_pages", None)
100
+ ast = service._ast_service
101
+ if not ast.repair_changed_files(expected_manifest=manifest):
102
+ ast.stop_hydration()
103
+ if previous.keys() != manifest.keys():
104
+ ast._context_mgr.discover_files()
105
+ for path in changed:
106
+ if path not in manifest:
107
+ ast._store.set_meta("structural_snapshot_v1", "")
108
+ ast._snapshot_start = None
109
+ ast._index.remove_file(path)
110
+ ast._ast_cache.pop(path, None)
111
+ ast._reference_indexed_files.discard(path)
112
+ else:
113
+ ast.notify_file_changed(path)
114
+ graph = ast._context_mgr.dependency_graph
115
+ if graph:
116
+ ast._index.file_dependencies.clear()
117
+ ast._index.file_dependents.clear()
118
+ for source, targets in graph.forward.items():
119
+ for target in targets:
120
+ ast._index.add_file_dependency(source, target)
121
+ if ast._hydration_state and ast._hydration_state.phase != "ready":
122
+ ast.start_hydration()
123
+ for path in changed:
124
+ if service._semantic_search:
125
+ service._semantic_search.invalidate_file(str(Path(self.root) / path))
126
+ service._semantic_search._kw_index_built = False
127
+ self.manifest = manifest
128
+
129
+
130
+ class WorkspaceWatcher:
131
+ """Watch only; refreshes run on the workspace's serial worker."""
132
+
133
+ def __init__(self, root, callback, debounce=500):
134
+ self.stop_event = threading.Event()
135
+ self.thread = threading.Thread(
136
+ target=self._run, args=(root, callback, debounce), daemon=True, name="intel-watch"
137
+ )
138
+ self.thread.start()
139
+
140
+ def _run(self, root, callback, debounce):
141
+ try:
142
+ from watchfiles import watch
143
+
144
+ for changes in watch(root, stop_event=self.stop_event, debounce=max(debounce, 50)):
145
+ if any(".attocode" not in Path(path).parts for _, path in changes):
146
+ callback()
147
+ except Exception:
148
+ logging.getLogger(__name__).warning(
149
+ "File watcher unavailable; per-operation stat checks remain active", exc_info=True
150
+ )
151
+
152
+ def close(self):
153
+ self.stop_event.set()
154
+ self.thread.join(timeout=2)