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,258 @@
1
+ """Reversible client configuration and end-to-end installation diagnostics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import sys
9
+ import tempfile
10
+ from pathlib import Path
11
+
12
+ import tomlkit
13
+
14
+ from attocode_intel.catalog import INSTRUCTIONS
15
+ from attocode_intel.project_dir import cache_folder
16
+
17
+ NAME = "attocode-code-intel"
18
+ REMOTE_NAME = NAME + "-remote"
19
+ START = "<!-- attocode-code-intel:start -->"
20
+ END = "<!-- attocode-code-intel:end -->"
21
+
22
+
23
+ def client_paths(client: str, project: Path, global_scope: bool):
24
+ home = Path.home()
25
+ if client == "codex":
26
+ base = home / ".codex" if global_scope else project / ".codex"
27
+ return (
28
+ base / "config.toml",
29
+ (base / "AGENTS.md" if global_scope else project / "AGENTS.md"),
30
+ "mcp_servers",
31
+ )
32
+ if client == "claude":
33
+ return (
34
+ (home / ".claude.json" if global_scope else project / ".mcp.json"),
35
+ (home / ".claude/CLAUDE.md" if global_scope else project / "CLAUDE.md"),
36
+ "mcpServers",
37
+ )
38
+ if client == "cursor":
39
+ return (
40
+ (home if global_scope else project) / ".cursor/mcp.json",
41
+ project / ".cursor/rules/attocode-code-intel.mdc",
42
+ "mcpServers",
43
+ )
44
+ raise ValueError(f"Unsupported client: {client}")
45
+
46
+
47
+ def atomic_write(path: Path, text: str):
48
+ path.parent.mkdir(parents=True, exist_ok=True)
49
+ mode = path.stat().st_mode & 0o777 if path.exists() else 0o600
50
+ fd, temporary = tempfile.mkstemp(prefix=".intel-", dir=path.parent)
51
+ try:
52
+ with os.fdopen(fd, "w", encoding="utf-8") as stream:
53
+ stream.write(text)
54
+ stream.flush()
55
+ os.fsync(stream.fileno())
56
+ os.chmod(temporary, mode)
57
+ os.replace(temporary, path)
58
+ finally:
59
+ if os.path.exists(temporary):
60
+ os.unlink(temporary)
61
+
62
+
63
+ def _instruction_text(path: Path, client: str, remove=False):
64
+ text = path.read_text() if path.exists() else ""
65
+ if (START in text) != (END in text):
66
+ raise ValueError(f"Incomplete managed instruction block in {path}")
67
+ block = f"{START}\n{INSTRUCTIONS}\n{END}"
68
+ if START in text:
69
+ first = text.index(START)
70
+ last = text.index(END, first) + len(END)
71
+ text = text[:first] + ("" if remove else block) + text[last:]
72
+ elif not remove:
73
+ if not text and client == "cursor":
74
+ text = "---\nalwaysApply: true\n---\n"
75
+ text = text.rstrip() + "\n\n" + block + "\n"
76
+ return text
77
+
78
+
79
+ def configure_client(
80
+ client,
81
+ project=".",
82
+ *,
83
+ global_scope=False,
84
+ profile="daily",
85
+ dry_run=False,
86
+ remove=False,
87
+ server="",
88
+ token_env="ATTOCODE_API_KEY",
89
+ repo="",
90
+ ):
91
+ if repo and not server and not remove:
92
+ raise ValueError("Remote installation requires --server; use --remote only with doctor or --remove")
93
+ root = Path(project).resolve()
94
+ config, guidance, key = client_paths(client, root, global_scope)
95
+ entry_name = REMOTE_NAME if server or repo else NAME
96
+ state_path = config.parent / ".attocode-intelligence-install.json"
97
+ # The Claude project config sits in the project root, where the record would show in git status.
98
+ old_state = None
99
+ if config.parent == root:
100
+ old_state, state_path = state_path, root / ".attocode" / "claude-install.json"
101
+ source = state_path if state_path.exists() or not (old_state and old_state.exists()) else old_state
102
+ state = json.loads(source.read_text()) if source.exists() else {}
103
+ is_toml = config.suffix == ".toml"
104
+ if config.exists():
105
+ raw = config.read_text()
106
+ document = tomlkit.parse(raw) if is_toml else json.loads(raw)
107
+ if not isinstance(document, dict) and not is_toml:
108
+ raise ValueError(f"Expected an object in {config}")
109
+ else:
110
+ document = tomlkit.document() if is_toml else {}
111
+ servers = document.setdefault(key, {})
112
+ if remove:
113
+ saved = state.get(entry_name)
114
+ if saved and servers.get(entry_name) != saved["installed"]:
115
+ raise ValueError(
116
+ f"{entry_name} was edited after installation; preserve those edits before removing it"
117
+ )
118
+ if saved and saved["previous"] is not None:
119
+ servers[entry_name] = saved["previous"]
120
+ else:
121
+ servers.pop(entry_name, None)
122
+ state.pop(entry_name, None)
123
+ elif server:
124
+ from urllib.parse import urlsplit
125
+
126
+ url = urlsplit(server)
127
+ if url.scheme not in ("http", "https") or not url.netloc or url.username or url.password:
128
+ raise ValueError("Server must be an HTTP(S) URL without embedded credentials")
129
+ endpoint = server.rstrip("/")
130
+ entry = {"url": endpoint + ("/" if endpoint.endswith("/mcp") else "/mcp/")}
131
+ if client == "claude":
132
+ entry["type"] = "http"
133
+ if client == "codex":
134
+ entry["bearer_token_env_var"] = token_env
135
+ else:
136
+ env_ref = "${" + token_env + "}" if client == "claude" else "${env:" + token_env + "}"
137
+ entry["headers"] = {"Authorization": "Bearer " + env_ref}
138
+ if repo:
139
+ entry.setdefault("http_headers" if client == "codex" else "headers", {})[
140
+ "X-Attocode-Workspace"
141
+ ] = repo
142
+ entry.setdefault("http_headers" if client == "codex" else "headers", {})[
143
+ "X-Attocode-Profile"
144
+ ] = profile
145
+ previous = state.get(entry_name, {}).get("previous", servers.get(entry_name))
146
+ state[entry_name] = {"previous": previous, "installed": entry}
147
+ servers[entry_name] = entry
148
+ else:
149
+ executable = Path(sys.executable).parent / NAME
150
+ command = str(executable) if executable.is_file() else None
151
+ entry = {
152
+ "command": command or sys.executable,
153
+ "args": ([] if command else ["-m", "attocode_intel.entrypoint"])
154
+ + ["--profile", profile],
155
+ }
156
+ if not global_scope:
157
+ entry["args"] += ["--project", str(root)]
158
+ if client == "cursor":
159
+ entry["type"] = "stdio"
160
+ previous = state.get(entry_name, {}).get("previous", servers.get(entry_name))
161
+ state[entry_name] = {"previous": previous, "installed": entry}
162
+ servers[entry_name] = entry
163
+ output = tomlkit.dumps(document) if is_toml else json.dumps(document, indent=2) + "\n"
164
+ # Claude Code shows the server instructions to the model, so a CLAUDE.md copy is a duplicate
165
+ # in every request. Claude gets no managed block, and an old block is removed.
166
+ drop = client == "claude" or (remove and not any(name in servers for name in (NAME, REMOTE_NAME)))
167
+ instruction = _instruction_text(guidance, client, remove=drop)
168
+ # Prepare everything before writing either file; previews contain no credential values.
169
+ if dry_run:
170
+ return {
171
+ "config": str(config),
172
+ "entry": entry_name,
173
+ "configuration": servers.get(entry_name),
174
+ "guidance": str(guidance),
175
+ "instructions": instruction,
176
+ }
177
+ atomic_write(config, output)
178
+ if guidance.exists() or not (remove or drop):
179
+ atomic_write(guidance, instruction)
180
+ if old_state:
181
+ cache_folder(root)
182
+ atomic_write(state_path, json.dumps(state, indent=2) + "\n")
183
+ if old_state and old_state.exists():
184
+ old_state.unlink()
185
+ return {"client": client, "config": str(config), "guidance": str(guidance), "removed": remove}
186
+
187
+
188
+ async def diagnose(client, project=".", *, global_scope=False, remote=False):
189
+ import httpx
190
+ from mcp import ClientSession, StdioServerParameters
191
+ from mcp.client.stdio import stdio_client
192
+ from mcp.client.streamable_http import streamable_http_client
193
+
194
+ root = Path(project).resolve()
195
+ config, _, key = client_paths(client, root, global_scope)
196
+ name = REMOTE_NAME if remote else NAME
197
+ text = config.read_text() if config.exists() else ""
198
+ doc = (tomlkit.parse(text) if config.suffix == ".toml" else json.loads(text)) if text else {}
199
+ entry = doc.get(key, {}).get(name)
200
+ if not entry:
201
+ return {"client": client, "status": "not configured", "config": str(config),
202
+ "hint": f"Run: attocode-code-intel init --client {client}"}
203
+
204
+ async def probe(streams):
205
+ async with ClientSession(streams[0], streams[1]) as session:
206
+ await session.initialize()
207
+ catalog = await session.list_tools()
208
+ result = await session.call_tool(
209
+ "capabilities", {"max_tokens": 16000, **({"workspace": str(root)} if "url" not in entry else {})}
210
+ )
211
+ if result.isError:
212
+ raise RuntimeError(result.content)
213
+ payload = result.structuredContent
214
+ if payload is None:
215
+ payload = json.loads(next(item.text for item in result.content if item.type == "text"))
216
+ metadata = payload.get("metadata", {})
217
+ if "url" not in entry and metadata.get("workspace") != str(root):
218
+ raise RuntimeError("Server selected a different workspace")
219
+ query = await session.call_tool(
220
+ "project_summary", {"workspace": metadata["workspace"], "max_tokens": 512}
221
+ )
222
+ if query.isError:
223
+ raise RuntimeError("Bounded project query failed")
224
+ return {
225
+ "client": client,
226
+ "tools": len(catalog.tools),
227
+ "metadata": metadata,
228
+ "status": "ok",
229
+ "languages": payload.get("capabilities", payload.get("data", {})).get("languages", {}),
230
+ "precision_hint": "Optional installed language servers improve reference lookup. Restart the agent after setup; ATTOCODE_INTEL_PRECISION=off disables enrichment.",
231
+ }
232
+
233
+ async with asyncio.timeout(30):
234
+ if "url" in entry:
235
+ headers = dict(entry.get("http_headers", entry.get("headers", {})))
236
+ for name, value in headers.items():
237
+ for env_name, env_value in os.environ.items():
238
+ value = value.replace("${env:" + env_name + "}", env_value).replace(
239
+ "${" + env_name + "}", env_value
240
+ )
241
+ headers[name] = value
242
+ if entry.get("bearer_token_env_var"):
243
+ token = os.environ.get(entry["bearer_token_env_var"], "")
244
+ headers["Authorization"] = "Bearer " + token
245
+ async with (
246
+ httpx.AsyncClient(headers=headers) as http,
247
+ streamable_http_client(entry["url"], http_client=http) as streams,
248
+ ):
249
+ return await probe(streams)
250
+ else:
251
+ params = StdioServerParameters(
252
+ command=entry["command"],
253
+ args=list(entry.get("args", [])),
254
+ env={**os.environ, **dict(entry.get("env", {}))},
255
+ cwd=str(root),
256
+ )
257
+ async with stdio_client(params) as streams:
258
+ return await probe(streams)
@@ -0,0 +1,178 @@
1
+ """A single output budget, including all composed sections."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from copy import deepcopy
7
+
8
+ from mcp import types
9
+
10
+ from attocode_intel._internal.integrations.utilities.token_estimate import count_tokens
11
+
12
+
13
+ def bounded_text(text: str, max_tokens: int) -> tuple[str, bool]:
14
+ if max_tokens < 1:
15
+ raise ValueError("max_tokens must be positive")
16
+ if count_tokens(text) <= max_tokens:
17
+ return text, False
18
+ suffix = "\n[Truncated; narrow the query or increase max_tokens.]"
19
+ if count_tokens(suffix) >= max_tokens:
20
+ suffix = ""
21
+ lo, hi = 0, len(text)
22
+ while lo < hi:
23
+ mid = (lo + hi + 1) // 2
24
+ if count_tokens(text[:mid] + suffix) <= max_tokens:
25
+ lo = mid
26
+ else:
27
+ hi = mid - 1
28
+ return text[:lo] + suffix, True
29
+
30
+
31
+ def compact_metadata(metadata: dict) -> dict:
32
+ """Provenance is mandatory; detailed diagnostics live in capabilities."""
33
+ result = {key: metadata[key] for key in
34
+ ("workspace", "workspaces", "warming_workspaces", "source", "revision",
35
+ "freshness", "truncated", "ranking")
36
+ if key in metadata}
37
+ coverage = metadata.get("coverage", {})
38
+ analysis = metadata.get("analysis", {})
39
+ if coverage:
40
+ result["index"] = {key: coverage[key] for key in
41
+ ("phase", "parsed_files", "total_files", "discovery_truncated") if key in coverage}
42
+ if coverage.get("excluded_checkout_roots"):
43
+ result["index"]["excluded_checkout_roots_found"] = len(coverage["excluded_checkout_roots"])
44
+ if coverage.get("excluded_checkout_roots_truncated"):
45
+ result["index"]["excluded_checkout_roots_truncated"] = True
46
+ if analysis:
47
+ result["analysis"] = analysis.get("status", "partial")
48
+ result["absence_proven"] = False
49
+ result["precision"] = analysis.get("precision", {}).get("status", "not_requested")
50
+ return result
51
+
52
+
53
+ def compact_result(metadata: dict, data) -> types.CallToolResult:
54
+ # Text-only MCP works in clients that ignore structuredContent and avoids duplication
55
+ # in clients that include both representations in the model's context.
56
+ text = json.dumps({"metadata": metadata, "data": data}, ensure_ascii=False, separators=(",", ":"))
57
+ return types.CallToolResult(content=[types.TextContent(type="text", text=text)])
58
+
59
+
60
+ def response_tokens(result: types.CallToolResult) -> int:
61
+ return count_tokens(result.model_dump_json(exclude_none=True))
62
+
63
+
64
+ def bounded_bootstrap(metadata: dict, body: str, max_tokens: int) -> types.CallToolResult:
65
+ """Fit whole bootstrap sections into the serialized MCP budget."""
66
+ if not body.startswith("## "):
67
+ return bounded_compact(metadata, {"result": body}, max_tokens)
68
+ sections = ["## " + section for section in body.removeprefix("## ").split("\n\n## ")]
69
+ compact = compact_metadata(metadata)
70
+ probe_metadata = {**compact, "truncated": True}
71
+ selected: list[str] = []
72
+ omitted_search = False
73
+ for section in sections:
74
+ candidate = "\n\n".join([*selected, section])
75
+ if response_tokens(compact_result(probe_metadata, {"result": candidate})) <= max_tokens:
76
+ selected.append(section)
77
+ elif section.startswith("## Relevant Code"):
78
+ # Search hits are indivisible evidence. Never let the generic string
79
+ # shortener turn a ranked result into a plausible-looking fragment.
80
+ omission = "## Relevant Code\nTop match omitted; increase max_tokens."
81
+ candidate = "\n\n".join([*selected, omission])
82
+ if response_tokens(compact_result(probe_metadata, {"result": candidate})) <= max_tokens:
83
+ selected.append(omission)
84
+ omitted_search = True
85
+ else:
86
+ raise ValueError("max_tokens is too small to report omitted search evidence; increase it")
87
+ if not selected:
88
+ return bounded_compact(metadata, {"result": body}, max_tokens)
89
+ compact["truncated"] = compact.get("truncated", False) or omitted_search or len(selected) != len(sections)
90
+ return compact_result(compact, {"result": "\n\n".join(selected)})
91
+
92
+
93
+ def bounded_compact(metadata: dict, data, max_tokens: int) -> types.CallToolResult:
94
+ """Bound the serialized result while retaining valid data and its provenance.
95
+
96
+ The estimate uses the shared tokenizer, not a promise about every client's tokenizer.
97
+ Only evidence collections and prose are shortened; paths, identifiers and flags survive.
98
+ """
99
+ metadata = compact_metadata(metadata)
100
+ data = deepcopy(data)
101
+ omitted = {}
102
+ match data:
103
+ case {"results": list() as result_rows,
104
+ "ranking": {"candidate_pool_count": _, **ranking}}:
105
+ search_result_count = len(result_rows)
106
+ initial_search_omissions = ranking.get("omitted_results_due_to_budget", 0)
107
+ case _:
108
+ search_result_count = None
109
+ initial_search_omissions = 0
110
+ while True:
111
+ if search_result_count is not None:
112
+ delivered = len(data["results"])
113
+ if delivered < search_result_count:
114
+ data["ranking"]["delivered_count"] = delivered
115
+ data["ranking"]["omitted_results_due_to_budget"] = (
116
+ initial_search_omissions + search_result_count - delivered
117
+ )
118
+ metadata["ranking"] = deepcopy(data["ranking"])
119
+ metadata["truncated"] = True
120
+ # Budget trimming can shorten a source page after inspection constructed its
121
+ # continuation. Compute the next offset from the excerpt actually delivered.
122
+ if (isinstance(data, dict) and isinstance(data.get("follow_up"), dict) and "next_source" in data["follow_up"]
123
+ and "source" in data and "definition" in data):
124
+ source, definition = data["source"], data["definition"]
125
+ if source["end_line"] < source["start_line"]:
126
+ raise ValueError("max_tokens is too small to include a complete source line; increase it")
127
+ data["follow_up"]["next_source"] = ({
128
+ "tool": "inspect_symbol", "symbol_name": definition["qualified_name"],
129
+ "file_path": definition["file_path"], "line": definition["start_line"],
130
+ "source_start_line": source["end_line"] + 1,
131
+ } if source["end_line"] < definition["end_line"] else None)
132
+ result = compact_result(metadata, data)
133
+ if response_tokens(result) <= max_tokens:
134
+ return result
135
+ choices = []
136
+
137
+ def visit(value, parent=None, key=None, path="data", choices=choices):
138
+ if isinstance(value, list) and value:
139
+ # A partial match explanation would falsely imply that omitted
140
+ # query terms did not match. Drop whole hits before splitting it.
141
+ if key not in {"matched_terms", "match_fields"}:
142
+ choices.append((len(json.dumps(value)), parent, key, value, path))
143
+ elif isinstance(value, str) and (key in {"text", "snippet", "result", "details", "description"}):
144
+ if len(value) > 32:
145
+ choices.append((len(value), parent, key, value, path))
146
+ elif isinstance(value, dict):
147
+ if key == "languages" and value:
148
+ choices.append((len(json.dumps(value)), parent, key, value, path))
149
+ else:
150
+ for child, item in value.items():
151
+ if child not in {"omitted", "follow_up", "definition"}:
152
+ visit(item, value, child, path + "." + child)
153
+
154
+ wrapper = {"data": data}
155
+ visit(data, wrapper, "data")
156
+ if not choices:
157
+ raise ValueError("max_tokens is too small to preserve provenance and evidence identifiers; increase it")
158
+ _, parent, key, value, path = max(choices, key=lambda item: item[0])
159
+ if isinstance(value, list):
160
+ keep = len(value) // 2
161
+ parent[key] = value[:keep]
162
+ omitted[path] = omitted.get(path, 0) + len(value) - keep
163
+ elif isinstance(value, dict):
164
+ keep = len(value) // 2
165
+ parent[key] = dict(list(value.items())[:keep])
166
+ omitted[path] = omitted.get(path, 0) + len(value) - keep
167
+ else:
168
+ shortened = value[:len(value) // 2]
169
+ # Source excerpts must end on a complete line with accurate coordinates.
170
+ if key == "text" and "start_line" in parent:
171
+ shortened = shortened.rsplit("\n", 1)[0] if "\n" in shortened else ""
172
+ parent["end_line"] = parent["start_line"] + len(shortened.splitlines()) - 1
173
+ parent["truncated"] = True
174
+ parent[key] = shortened
175
+ omitted[path] = "shortened"
176
+ data = wrapper["data"]
177
+ metadata["truncated"] = True
178
+ metadata["omitted"] = omitted
@@ -0,0 +1,133 @@
1
+ """Optional workspace-scoped language-server enrichment for daily navigation."""
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import os
6
+ import shutil
7
+ from pathlib import Path
8
+
9
+ from ._internal.integrations.context.codebase_ast import detect_language
10
+ from ._internal.integrations.context.syntax_evidence import symbol_position
11
+
12
+
13
+ class PrecisionSession:
14
+ def __init__(self, service):
15
+ self.service = service
16
+ self.documents = {}
17
+ self.pending = set()
18
+ self.completed = {}
19
+ self.failures = {}
20
+ self.verified_languages = set()
21
+
22
+ def invalidate(self, paths):
23
+ self.pending.update(paths)
24
+ self.completed.clear()
25
+ self.failures.clear()
26
+
27
+ async def enrich(self, name, file_path=None, line=None):
28
+ if os.environ.get("ATTOCODE_INTEL_PRECISION", "auto") == "off":
29
+ return {"status": "disabled"}
30
+ from .symbol_inspection import select_definitions
31
+ ast = self.service._get_ast_service()
32
+ definitions = select_definitions(self.service, name, file_path, line)
33
+ if not definitions:
34
+ return {"status": "no_definition"}
35
+ if len(definitions) != 1:
36
+ return {"status": "ambiguous", "definitions": len(definitions)}
37
+ key = tuple((d.file_path, d.start_line, d.qualified_name) for d in definitions)
38
+ if key in self.completed:
39
+ return self.completed[key]
40
+ from ._internal.integrations.lsp.client import BUILTIN_SERVERS
41
+ outcomes = []
42
+ deadline = asyncio.get_running_loop().time() + 2.0
43
+ for definition in definitions[:8]:
44
+ language = detect_language(definition.file_path)
45
+ server_id = "typescript" if language == "javascript" else language
46
+ config = BUILTIN_SERVERS.get(server_id)
47
+ if not config or not shutil.which(config.command):
48
+ outcomes.append({"language": language, "status": "unavailable"})
49
+ continue
50
+ if server_id in self.failures:
51
+ outcomes.append({"language": language, "status": self.failures[server_id]})
52
+ continue
53
+ manager = self.service._get_lsp_manager()
54
+ try:
55
+ async with asyncio.timeout_at(deadline):
56
+ if server_id not in manager.get_active_servers():
57
+ manager._timeout = 2
58
+ await manager.start_server(server_id, Path(self.service.project_dir).as_uri())
59
+ # Send edits/deletions for all previously opened documents.
60
+ for rel in list(self.pending):
61
+ path = str(Path(self.service.project_dir) / rel)
62
+ if path in self.documents:
63
+ if Path(path).is_file():
64
+ self.documents[path] += 1
65
+ manager.notify_file_changed(path, Path(path).read_text(), self.documents[path])
66
+ else:
67
+ manager.notify_file_closed(path)
68
+ self.documents.pop(path)
69
+ elif Path(path).is_file() and manager._get_client_for_file(path):
70
+ manager.notify_file_opened(path, Path(path).read_text())
71
+ self.documents[path] = 1
72
+ self.pending.discard(rel)
73
+ path = str(Path(self.service.project_dir) / definition.file_path)
74
+ if path not in self.documents:
75
+ manager.notify_file_opened(path, Path(path).read_text())
76
+ self.documents[path] = 1
77
+ position = symbol_position(path, definition.name, definition.start_line,
78
+ definition.end_line, language)
79
+ if position is None:
80
+ outcomes.append({"language": language, "status": "identifier_unavailable"})
81
+ continue
82
+ # Supply the explicit queried symbol; body ranges alone are ambiguous.
83
+ callback = manager.on_result_callback
84
+ manager.on_result_callback = None
85
+ try:
86
+ resolved = await manager.get_definition(path, *position)
87
+ if resolved is None:
88
+ outcomes.append({"language": language, "status": "not_resolved"})
89
+ continue
90
+ references = await manager.get_references(path, *position, include_declaration=False)
91
+ # Some servers initially know only the opened definition. Prime a
92
+ # bounded set of indexed candidates, then retry once within the same
93
+ # deadline. No sleeps, background warmup, or unbounded file reads.
94
+ candidates = ast.get_callers(definition.qualified_name)
95
+ if not references and candidates:
96
+ root = Path(self.service.project_dir).resolve()
97
+ remaining_bytes = 256_000
98
+ for rel in sorted({ref.file_path for ref in candidates})[:8]:
99
+ if asyncio.get_running_loop().time() >= deadline:
100
+ break
101
+ candidate = (root / rel).resolve()
102
+ if (not candidate.is_relative_to(root) or not candidate.is_file()
103
+ or str(candidate) in self.documents
104
+ or not manager._get_client_for_file(str(candidate))):
105
+ continue
106
+ size = candidate.stat().st_size
107
+ if size > min(64_000, remaining_bytes):
108
+ continue
109
+ with candidate.open("rb") as stream:
110
+ content = stream.read(min(64_000, remaining_bytes) + 1)
111
+ if len(content) > min(64_000, remaining_bytes):
112
+ continue
113
+ manager.notify_file_opened(str(candidate), content.decode("utf-8", errors="replace"))
114
+ self.documents[str(candidate)] = 1
115
+ remaining_bytes -= len(content)
116
+ references = await manager.get_references(path, *position, include_declaration=False)
117
+ finally:
118
+ manager.on_result_callback = callback
119
+ ast.ingest_lsp_results("references", path, references,
120
+ {"line": position[0], "col": position[1],
121
+ "symbol_name": definition.qualified_name})
122
+ self.verified_languages.add(language)
123
+ outcomes.append({"language": language, "status": "verified" if references or not candidates else "no_references",
124
+ "symbol": definition.qualified_name, "references": len(references)})
125
+ except Exception as exc:
126
+ status = "timeout" if isinstance(exc, TimeoutError) or isinstance(exc.__cause__, TimeoutError) else "failed"
127
+ self.failures[server_id] = status
128
+ outcomes.append({"language": language, "status": status})
129
+ status = "verified" if outcomes and all(o["status"] == "verified" for o in outcomes) else "partial"
130
+ result = {"status": status, "queries": outcomes,
131
+ "definitions_truncated": len(definitions) > 8}
132
+ self.completed[key] = result
133
+ return result