asicode 0.2.6__tar.gz → 0.2.7__tar.gz

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 (360) hide show
  1. {asicode-0.2.6/asicode.egg-info → asicode-0.2.7}/PKG-INFO +1 -1
  2. {asicode-0.2.6 → asicode-0.2.7/asicode.egg-info}/PKG-INFO +1 -1
  3. {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/SOURCES.txt +3 -1
  4. asicode-0.2.7/external_llm/editor/_editor_core/common/strategy_priors.py +67 -0
  5. {asicode-0.2.6 → asicode-0.2.7}/pyproject.toml +1 -1
  6. asicode-0.2.7/utils/version_check.py +297 -0
  7. {asicode-0.2.6 → asicode-0.2.7}/LICENSE +0 -0
  8. {asicode-0.2.6 → asicode-0.2.7}/README.md +0 -0
  9. {asicode-0.2.6 → asicode-0.2.7}/asi.py +0 -0
  10. {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/dependency_links.txt +0 -0
  11. {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/entry_points.txt +0 -0
  12. {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/requires.txt +0 -0
  13. {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/top_level.txt +0 -0
  14. {asicode-0.2.6 → asicode-0.2.7}/common.py +0 -0
  15. {asicode-0.2.6 → asicode-0.2.7}/config.py +0 -0
  16. {asicode-0.2.6 → asicode-0.2.7}/context_collector.py +0 -0
  17. {asicode-0.2.6 → asicode-0.2.7}/diff_apply.py +0 -0
  18. {asicode-0.2.6 → asicode-0.2.7}/external_llm/__init__.py +0 -0
  19. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/__init__.py +0 -0
  20. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/_shared_utils.py +0 -0
  21. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/_thread_pool.py +0 -0
  22. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/_user_intent.py +0 -0
  23. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_context_manager.py +0 -0
  24. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_escalation_pipeline.py +0 -0
  25. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_fast_path.py +0 -0
  26. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_loop.py +0 -0
  27. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_loop_types.py +0 -0
  28. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_phase_manager.py +0 -0
  29. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_planner_pipeline.py +0 -0
  30. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_profile.py +0 -0
  31. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_turn_pipeline.py +0 -0
  32. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/alignment_scorer.py +0 -0
  33. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/anchor_shared.py +0 -0
  34. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/argument_repairer.py +0 -0
  35. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/ast_cache.py +0 -0
  36. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/ast_engine.py +0 -0
  37. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/async_tool_executor.py +0 -0
  38. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/auto_correction.py +0 -0
  39. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/background_job_manager.py +0 -0
  40. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/call_graph.py +0 -0
  41. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/checkpoint_store.py +0 -0
  42. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/config/__init__.py +0 -0
  43. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/config/thresholds.py +0 -0
  44. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_budget.py +0 -0
  45. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_contract.py +0 -0
  46. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_manager.py +0 -0
  47. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_utils.py +0 -0
  48. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/design_chat_loop.py +0 -0
  49. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/enums.py +0 -0
  50. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/execution_mode_classifier.py +0 -0
  51. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/execution_spec.py +0 -0
  52. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/execution_thresholds.py +0 -0
  53. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/failure_classifier.py +0 -0
  54. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/failure_context.py +0 -0
  55. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/failure_pattern_store.py +0 -0
  56. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/file_cache.py +0 -0
  57. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/fix_spec_claim_validator.py +0 -0
  58. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/gsg_safety.py +0 -0
  59. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/guard_ir.py +0 -0
  60. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/handoff_observer.py +0 -0
  61. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/insights_manager.py +0 -0
  62. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/intent_models.py +0 -0
  63. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/intent_resolver.py +0 -0
  64. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/interrupt_tool_results.py +0 -0
  65. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/json_repair.py +0 -0
  66. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/language_backend.py +0 -0
  67. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/language_hint.py +0 -0
  68. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/learned_policy.py +0 -0
  69. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/light_repo_index.py +0 -0
  70. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/lint_runner.py +0 -0
  71. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/llm_output_utils.py +0 -0
  72. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/local_assistant.py +0 -0
  73. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/message_shapes.py +0 -0
  74. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/metadata_utils.py +0 -0
  75. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/operation_models.py +0 -0
  76. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/orchestrator.py +0 -0
  77. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/output_normalizer.py +0 -0
  78. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/performance_metrics.py +0 -0
  79. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/placement_contract.py +0 -0
  80. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/plan_state.py +0 -0
  81. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/planner_lane_facade.py +0 -0
  82. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/rag_configs.py +0 -0
  83. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/rag_searcher.py +0 -0
  84. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/reasoning_utils.py +0 -0
  85. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/repair_helpers.py +0 -0
  86. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/request_intent_classifier.py +0 -0
  87. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/routing_policy.py +0 -0
  88. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/run_store.py +0 -0
  89. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/scanner_registry.py +0 -0
  90. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/selector_constants.py +0 -0
  91. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/semantic_intent.py +0 -0
  92. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/semantic_lint.py +0 -0
  93. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/session_state.py +0 -0
  94. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/slash_commands.py +0 -0
  95. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/subagent_ipc.py +0 -0
  96. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_engine.py +0 -0
  97. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_index.py +0 -0
  98. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_locator.py +0 -0
  99. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_modify_tool.py +0 -0
  100. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_search.py +0 -0
  101. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/task_router.py +0 -0
  102. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/terminal_coordination.py +0 -0
  103. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/termination_policy.py +0 -0
  104. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/test_impact_selector.py +0 -0
  105. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/test_runner.py +0 -0
  106. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_chain.py +0 -0
  107. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_dependency_graph.py +0 -0
  108. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_failure_log.py +0 -0
  109. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/__init__.py +0 -0
  110. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/agent_tools.py +0 -0
  111. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/analysis_tools.py +0 -0
  112. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/ast_op_executor.py +0 -0
  113. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/browser_tools.py +0 -0
  114. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/constants.py +0 -0
  115. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/git_tools.py +0 -0
  116. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/read_tools.py +0 -0
  117. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/shell_policy.py +0 -0
  118. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/test_tools.py +0 -0
  119. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/web_search_tools.py +0 -0
  120. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/write_tools.py +0 -0
  121. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_registry.py +0 -0
  122. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_result_cache.py +0 -0
  123. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_safety.py +0 -0
  124. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_schemas.py +0 -0
  125. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/vector_cache.py +0 -0
  126. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/weight_learning.py +0 -0
  127. {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/work_state_digest.py +0 -0
  128. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/__init__.py +0 -0
  129. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/_dead_block_shared.py +0 -0
  130. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/ast_similarity_scanner.py +0 -0
  131. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/broken_contract_scanner.py +0 -0
  132. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/container_reachability_scanner.py +0 -0
  133. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/contradictory_logic_scanner.py +0 -0
  134. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/cross_file_refs.py +0 -0
  135. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/dead_block_scanner.py +0 -0
  136. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/duplicate_definition_scanner.py +0 -0
  137. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/parse_cache.py +0 -0
  138. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/public_dead_code_scanner.py +0 -0
  139. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/unused_import_scanner.py +0 -0
  140. {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/vulture_scanner.py +0 -0
  141. {asicode-0.2.6 → asicode-0.2.7}/external_llm/anthropic_client.py +0 -0
  142. {asicode-0.2.6 → asicode-0.2.7}/external_llm/ast_rewrite.py +0 -0
  143. {asicode-0.2.6 → asicode-0.2.7}/external_llm/client.py +0 -0
  144. {asicode-0.2.6 → asicode-0.2.7}/external_llm/code_analyzer.py +0 -0
  145. {asicode-0.2.6 → asicode-0.2.7}/external_llm/code_structure_utils.py +0 -0
  146. {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/__init__.py +0 -0
  147. {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/atomic_io.py +0 -0
  148. {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/file_lock.py +0 -0
  149. {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/indent_utils.py +0 -0
  150. {asicode-0.2.6 → asicode-0.2.7}/external_llm/context/context_packs.py +0 -0
  151. {asicode-0.2.6 → asicode-0.2.7}/external_llm/context_builder.py +0 -0
  152. {asicode-0.2.6 → asicode-0.2.7}/external_llm/dependency_graph.py +0 -0
  153. {asicode-0.2.6 → asicode-0.2.7}/external_llm/design_session.py +0 -0
  154. {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/__init__.py +0 -0
  155. {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/dataflow_extractor.py +0 -0
  156. {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/graph_propagator.py +0 -0
  157. {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/relevance_scorer.py +0 -0
  158. {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/request_analyzer.py +0 -0
  159. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/__init__.py +0 -0
  160. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/common/__init__.py +0 -0
  161. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/common/import_normalizer.py +0 -0
  162. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/__init__.py +0 -0
  163. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/__init__.py +0 -0
  164. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/ast_rewriter.py +0 -0
  165. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/models.py +0 -0
  166. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/rollback_manager.py +0 -0
  167. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/verifier.py +0 -0
  168. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/vm.py +0 -0
  169. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/learning/__init__.py +0 -0
  170. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/learning/exploration_policy.py +0 -0
  171. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/primitives/__init__.py +0 -0
  172. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/primitives/executor.py +0 -0
  173. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/primitives/models.py +0 -0
  174. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/__init__.py +0 -0
  175. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/failure_classifier.py +0 -0
  176. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/repair_planner.py +0 -0
  177. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/repair_registry.py +0 -0
  178. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/repair_strategies.py +0 -0
  179. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/__init__.py +0 -0
  180. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/ast_rewriter.py +0 -0
  181. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/classification.py +0 -0
  182. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/__init__.py +0 -0
  183. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/contract_diff.py +0 -0
  184. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/contract_extractor.py +0 -0
  185. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/contract_models.py +0 -0
  186. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/project_graph.py +0 -0
  187. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/propagation_planner.py +0 -0
  188. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/failure_classifier.py +0 -0
  189. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/models.py +0 -0
  190. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/repair_planner.py +0 -0
  191. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/repair_registry.py +0 -0
  192. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/repair_strategies.py +0 -0
  193. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/rollback_manager.py +0 -0
  194. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/verifier.py +0 -0
  195. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/vm.py +0 -0
  196. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/__init__.py +0 -0
  197. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/proactive_runner.py +0 -0
  198. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/push_manager.py +0 -0
  199. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/task_queue.py +0 -0
  200. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/trigger_engine.py +0 -0
  201. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/trigger_policy.py +0 -0
  202. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/mcp/__init__.py +0 -0
  203. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/mcp/server.py +0 -0
  204. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/prompts/__init__.py +0 -0
  205. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/prompts/edit_prompts.py +0 -0
  206. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/__init__.py +0 -0
  207. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/models.py +0 -0
  208. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/strategy_abstraction.py +0 -0
  209. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/transfer_engine.py +0 -0
  210. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/__init__.py +0 -0
  211. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/experience_store.py +0 -0
  212. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/exploration_state.py +0 -0
  213. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/learning_sink.py +0 -0
  214. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/pattern_extractor.py +0 -0
  215. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_models.py +0 -0
  216. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_scorer.py +0 -0
  217. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_store.py +0 -0
  218. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_updater.py +0 -0
  219. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_models.py +0 -0
  220. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_scorer.py +0 -0
  221. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_store.py +0 -0
  222. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_updater.py +0 -0
  223. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/problem_signature.py +0 -0
  224. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/strategy_execution_bias.py +0 -0
  225. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/strategy_prioritizer.py +0 -0
  226. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/strategy_state.py +0 -0
  227. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/unified_run_record.py +0 -0
  228. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/unified_store.py +0 -0
  229. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/__init__.py +0 -0
  230. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/code_context.py +0 -0
  231. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/delete_node.py +0 -0
  232. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/executor.py +0 -0
  233. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/insert_import.py +0 -0
  234. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/insert_statement.py +0 -0
  235. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/models.py +0 -0
  236. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/registry.py +0 -0
  237. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/rename_symbol.py +0 -0
  238. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/replace_function.py +0 -0
  239. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/update_call.py +0 -0
  240. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/__init__.py +0 -0
  241. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ast_transform_utils.py +0 -0
  242. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/contract_replan_strategy.py +0 -0
  243. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/draft_parser.py +0 -0
  244. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/flow_synthesizer.py +0 -0
  245. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/fragment_generator.py +0 -0
  246. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/fragment_integrator.py +0 -0
  247. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/generation_planner.py +0 -0
  248. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/libcst_transform_utils.py +0 -0
  249. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/output_comparator.py +0 -0
  250. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_code_templates.py +0 -0
  251. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_detector.py +0 -0
  252. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_ir_builder.py +0 -0
  253. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_models.py +0 -0
  254. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_reconstructor.py +0 -0
  255. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_registry.py +0 -0
  256. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contract_evaluator.py +0 -0
  257. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contract_models.py +0 -0
  258. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contract_registry.py +0 -0
  259. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contracts.py +0 -0
  260. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_corrector.py +0 -0
  261. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_gap_analyzer.py +0 -0
  262. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_rewrite_models.py +0 -0
  263. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_rewrite_planner.py +0 -0
  264. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_rewriter.py +0 -0
  265. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_tracer.py +0 -0
  266. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/synthesis_planner.py +0 -0
  267. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ts_ir_models.py +0 -0
  268. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ts_semantic_models.py +0 -0
  269. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ts_semantic_tracer.py +0 -0
  270. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/dependency_traversal.py +0 -0
  271. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/impact_models.py +0 -0
  272. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/impact_simulator.py +0 -0
  273. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/risk_estimator.py +0 -0
  274. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/__init__.py +0 -0
  275. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/code_integrity.py +0 -0
  276. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/impact_propagation.py +0 -0
  277. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/impact_verification_mapper.py +0 -0
  278. {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/verification_set_builder.py +0 -0
  279. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/composite_risk.py +0 -0
  280. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/execution_graph_advisor.py +0 -0
  281. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/graph_builder.py +0 -0
  282. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/graph_facade.py +0 -0
  283. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/models.py +0 -0
  284. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/repository_graph.py +0 -0
  285. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/run_scoped_graph_cache.py +0 -0
  286. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/spec_graph_enricher.py +0 -0
  287. {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/virtual_graph.py +0 -0
  288. {asicode-0.2.6 → asicode-0.2.7}/external_llm/hybrid_parser.py +0 -0
  289. {asicode-0.2.6 → asicode-0.2.7}/external_llm/image_utils.py +0 -0
  290. {asicode-0.2.6 → asicode-0.2.7}/external_llm/intelligent_service.py +0 -0
  291. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/__init__.py +0 -0
  292. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/base.py +0 -0
  293. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/bash_provider.py +0 -0
  294. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/capabilities.py +0 -0
  295. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/csharp_provider.py +0 -0
  296. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/css_provider.py +0 -0
  297. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/dependency_checker.py +0 -0
  298. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/go_provider.py +0 -0
  299. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/html_provider.py +0 -0
  300. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/java_provider.py +0 -0
  301. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/javascript_provider.py +0 -0
  302. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/json_provider.py +0 -0
  303. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/kotlin_provider.py +0 -0
  304. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/libcst_utils.py +0 -0
  305. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/models.py +0 -0
  306. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/php_provider.py +0 -0
  307. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/python_provider.py +0 -0
  308. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/registry.py +0 -0
  309. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/ruby_provider.py +0 -0
  310. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/rust_provider.py +0 -0
  311. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/swift_provider.py +0 -0
  312. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/syntax_validator.py +0 -0
  313. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/tree_sitter_utils.py +0 -0
  314. {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/typescript_provider.py +0 -0
  315. {asicode-0.2.6 → asicode-0.2.7}/external_llm/model_registry.py +0 -0
  316. {asicode-0.2.6 → asicode-0.2.7}/external_llm/multi_planner.py +0 -0
  317. {asicode-0.2.6 → asicode-0.2.7}/external_llm/ollama_api.py +0 -0
  318. {asicode-0.2.6 → asicode-0.2.7}/external_llm/openai_client.py +0 -0
  319. {asicode-0.2.6 → asicode-0.2.7}/external_llm/output_modes.py +0 -0
  320. {asicode-0.2.6 → asicode-0.2.7}/external_llm/output_parser.py +0 -0
  321. {asicode-0.2.6 → asicode-0.2.7}/external_llm/patch_engine.py +0 -0
  322. {asicode-0.2.6 → asicode-0.2.7}/external_llm/patch_synthesizer.py +0 -0
  323. {asicode-0.2.6 → asicode-0.2.7}/external_llm/project_analyzer.py +0 -0
  324. {asicode-0.2.6 → asicode-0.2.7}/external_llm/providers.py +0 -0
  325. {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/__init__.py +0 -0
  326. {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/asi_mcp_adapter.py +0 -0
  327. {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/claude_session.py +0 -0
  328. {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/cli.py +0 -0
  329. {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/collaboration_orchestrator.py +0 -0
  330. {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/streaming_display.py +0 -0
  331. {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/verdict.py +0 -0
  332. {asicode-0.2.6 → asicode-0.2.7}/external_llm/semantic_patch.py +0 -0
  333. {asicode-0.2.6 → asicode-0.2.7}/external_llm/service.py +0 -0
  334. {asicode-0.2.6 → asicode-0.2.7}/external_llm/smart_analyzer.py +0 -0
  335. {asicode-0.2.6 → asicode-0.2.7}/external_llm/super_context_builder.py +0 -0
  336. {asicode-0.2.6 → asicode-0.2.7}/external_llm/testing/__init__.py +0 -0
  337. {asicode-0.2.6 → asicode-0.2.7}/external_llm/testing/symbol_aware_test_finder.py +0 -0
  338. {asicode-0.2.6 → asicode-0.2.7}/external_llm/testing/test_dependency_graph.py +0 -0
  339. {asicode-0.2.6 → asicode-0.2.7}/patch_synth.py +0 -0
  340. {asicode-0.2.6 → asicode-0.2.7}/path_security.py +0 -0
  341. {asicode-0.2.6 → asicode-0.2.7}/plan_compiler.py +0 -0
  342. {asicode-0.2.6 → asicode-0.2.7}/services/__init__.py +0 -0
  343. {asicode-0.2.6 → asicode-0.2.7}/services/patch_helpers.py +0 -0
  344. {asicode-0.2.6 → asicode-0.2.7}/setup.cfg +0 -0
  345. {asicode-0.2.6 → asicode-0.2.7}/tests/test_common.py +0 -0
  346. {asicode-0.2.6 → asicode-0.2.7}/tests/test_config.py +0 -0
  347. {asicode-0.2.6 → asicode-0.2.7}/tests/test_context_budget.py +0 -0
  348. {asicode-0.2.6 → asicode-0.2.7}/tests/test_embedding_singleton.py +0 -0
  349. {asicode-0.2.6 → asicode-0.2.7}/tests/test_intent_classifier.py +0 -0
  350. {asicode-0.2.6 → asicode-0.2.7}/tests/test_learned_policy.py +0 -0
  351. {asicode-0.2.6 → asicode-0.2.7}/tests/test_ollama_api.py +0 -0
  352. {asicode-0.2.6 → asicode-0.2.7}/tests/test_path_security.py +0 -0
  353. {asicode-0.2.6 → asicode-0.2.7}/tests/test_progress_printer_inflight.py +0 -0
  354. {asicode-0.2.6 → asicode-0.2.7}/tests/test_real_agent.py +0 -0
  355. {asicode-0.2.6 → asicode-0.2.7}/tests/test_string_helper.py +0 -0
  356. {asicode-0.2.6 → asicode-0.2.7}/tests/test_work_state_digest.py +0 -0
  357. {asicode-0.2.6 → asicode-0.2.7}/tests/test_write_safety_repair_path.py +0 -0
  358. {asicode-0.2.6 → asicode-0.2.7}/utils/__init__.py +0 -0
  359. {asicode-0.2.6 → asicode-0.2.7}/utils/llm_utils.py +0 -0
  360. {asicode-0.2.6 → asicode-0.2.7}/utils/string_helper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asicode
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Autonomous Software Improvement — AST-precise code editing agent with verification gates (CLI + MCP server)
5
5
  Author-email: socialherb <junddory@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asicode
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Autonomous Software Improvement — AST-precise code editing agent with verification gates (CLI + MCP server)
5
5
  Author-email: socialherb <junddory@gmail.com>
6
6
  License-Expression: MIT
@@ -176,6 +176,7 @@ external_llm/edit_localization/request_analyzer.py
176
176
  external_llm/editor/_editor_core/__init__.py
177
177
  external_llm/editor/_editor_core/common/__init__.py
178
178
  external_llm/editor/_editor_core/common/import_normalizer.py
179
+ external_llm/editor/_editor_core/common/strategy_priors.py
179
180
  external_llm/editor/_editor_core/ts_vm/__init__.py
180
181
  external_llm/editor/_editor_core/ts_vm/execution_vm/__init__.py
181
182
  external_llm/editor/_editor_core/ts_vm/execution_vm/ast_rewriter.py
@@ -353,4 +354,5 @@ tests/test_work_state_digest.py
353
354
  tests/test_write_safety_repair_path.py
354
355
  utils/__init__.py
355
356
  utils/llm_utils.py
356
- utils/string_helper.py
357
+ utils/string_helper.py
358
+ utils/version_check.py
@@ -0,0 +1,67 @@
1
+ """Cold-start strategy priors — shared by the PLANNER lane strategy selector
2
+ and the live weight-learning cold-start bias (agent/weight_learning.py).
3
+
4
+ Lives in _editor_core/common (retained in the CLI-only distribution) so the
5
+ live consumer keeps working when the lane/ package is excluded."""
6
+ from __future__ import annotations
7
+
8
+ # Repair_burden is on a 0-3 scale (none=0, low=1, medium=2, high=3).
9
+ _COLD_PRIORS: dict[str, dict[str, float]] = {
10
+ "generic_create": {
11
+ "repair_burden": 0.50,
12
+ "success_rate": 0.80,
13
+ "contract_risk": 0.30,
14
+ "complexity": 0.30,
15
+ "cost": 0.20,
16
+ },
17
+ "reference_bound_create": {
18
+ "repair_burden": 0.30,
19
+ "success_rate": 0.85,
20
+ "contract_risk": 0.10,
21
+ "complexity": 0.40,
22
+ "cost": 0.25,
23
+ },
24
+ "symbol_guided_create": {
25
+ "repair_burden": 0.35,
26
+ "success_rate": 0.85,
27
+ "contract_risk": 0.15,
28
+ "complexity": 0.35,
29
+ "cost": 0.20,
30
+ },
31
+ "test_aware_create": {
32
+ "repair_burden": 0.40,
33
+ "success_rate": 0.82,
34
+ "contract_risk": 0.20,
35
+ "complexity": 0.35,
36
+ "cost": 0.25,
37
+ },
38
+ # P11: Modify/extend strategies
39
+ "symbol_edit": {
40
+ "repair_burden": 0.25,
41
+ "success_rate": 0.85,
42
+ "contract_risk": 0.15,
43
+ "complexity": 0.20,
44
+ "cost": 0.15,
45
+ },
46
+ "minimal_patch": {
47
+ "repair_burden": 0.15,
48
+ "success_rate": 0.88,
49
+ "contract_risk": 0.10,
50
+ "complexity": 0.10,
51
+ "cost": 0.10,
52
+ },
53
+ "refactor": {
54
+ "repair_burden": 0.60,
55
+ "success_rate": 0.70,
56
+ "contract_risk": 0.35,
57
+ "complexity": 0.55,
58
+ "cost": 0.30,
59
+ },
60
+ "test_first": {
61
+ "repair_burden": 0.35,
62
+ "success_rate": 0.80,
63
+ "contract_risk": 0.20,
64
+ "complexity": 0.30,
65
+ "cost": 0.25,
66
+ },
67
+ }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "asicode"
3
- version = "0.2.6"
3
+ version = "0.2.7"
4
4
  description = "Autonomous Software Improvement — AST-precise code editing agent with verification gates (CLI + MCP server)"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1,297 @@
1
+ """Background PyPI update check for the asicode CLI.
2
+
3
+ Once per ``interval`` (default 24h) the PyPI JSON API is queried for the latest
4
+ published version of the package; if it is newer than the running version a
5
+ one-line upgrade hint is produced. The network call runs on a daemon thread so
6
+ it never blocks CLI startup: a previously cached *latest* version is returned
7
+ immediately, and a fresh fetch merely refreshes the cache for the next run.
8
+
9
+ Design invariants
10
+ -----------------
11
+ * **Non-blocking.** The cache is read synchronously (cheap file read); the
12
+ network call is delegated to a daemon thread. ``collect(wait_s=0)`` never
13
+ blocks.
14
+ * **Crash-safe cache.** Writes use ``mkstemp`` + ``fsync`` + ``os.replace``
15
+ (POSIX-atomic). A corrupt / partially-written / unreadable cache is ignored
16
+ on read and silently overwritten on the next write (resilient load).
17
+ * **Testable.** The cache path is overridable via the
18
+ ``ASICODE_VERSION_CHECK_CACHE`` env var (mirrors the
19
+ ``ASICODE_WRITE_TOOL_FAILURE_LOG`` pattern), and the network call is pluggable
20
+ via the ``fetcher`` argument. No real network is touched by the unit tests.
21
+ * **Fail-open.** Any unexpected error is swallowed — the update check must
22
+ *never* break the CLI itself.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import json
28
+ import os
29
+ import tempfile
30
+ import threading
31
+ import time
32
+ from collections.abc import Callable
33
+ from pathlib import Path
34
+ from typing import Any, Optional
35
+
36
+ __all__ = [
37
+ "PACKAGE_NAME",
38
+ "UpdateCheckHandle",
39
+ "get_current_version",
40
+ "is_newer",
41
+ "start_update_check",
42
+ ]
43
+
44
+ # ─── configuration ───────────────────────────────────────────────────────────
45
+ PACKAGE_NAME = "asicode"
46
+ _PYPI_URL = f"https://pypi.org/pypi/{PACKAGE_NAME}/json"
47
+ # Network budget: PyPI JSON is tiny and fast; this is only a safety net against
48
+ # a hung connection (mirrors the subprocess-hang discipline of bounding reads).
49
+ _FETCH_TIMEOUT_S: float = 4.0
50
+ _DEFAULT_INTERVAL_HOURS: float = 24.0
51
+
52
+
53
+ # ─── current version ─────────────────────────────────────────────────────────
54
+ def get_current_version() -> str:
55
+ """Return the installed package version, or ``"0.0.0"`` if unresolvable.
56
+
57
+ Uses ``importlib.metadata`` (stdlib). On a source checkout that is not
58
+ installed, the metadata lookup fails — we return the ``"0.0.0"`` sentinel,
59
+ which :func:`start_update_check` treats as "not pip-installed" and skips
60
+ the check entirely (a pip-upgrade hint would be wrong there).
61
+ """
62
+ try:
63
+ from importlib.metadata import PackageNotFoundError, version
64
+
65
+ try:
66
+ return str(version(PACKAGE_NAME))
67
+ except PackageNotFoundError:
68
+ return "0.0.0"
69
+ except Exception:
70
+ return "0.0.0"
71
+
72
+
73
+ # ─── cache path ──────────────────────────────────────────────────────────────
74
+ def _cache_path() -> Path:
75
+ """Resolve the cache file path.
76
+
77
+ Honors ``ASICODE_VERSION_CHECK_CACHE`` (absolute path override, used by
78
+ tests to redirect to a temp file). Defaults to ``~/.asicode/version_check.json``.
79
+ """
80
+ override = os.environ.get("ASICODE_VERSION_CHECK_CACHE")
81
+ if override:
82
+ return Path(override)
83
+ return Path(os.path.expanduser("~")) / ".asicode" / "version_check.json"
84
+
85
+
86
+ def _read_cache() -> dict[str, Any]:
87
+ """Read & parse the cache. Returns ``{}`` if missing or corrupt.
88
+
89
+ Resilient load: a truncated/partial JSON file (e.g. crash mid-write of a
90
+ previous version that predated atomic writes, or external tampering) must
91
+ not raise — we treat it as an empty cache and let the next write repair it.
92
+ """
93
+ path = _cache_path()
94
+ try:
95
+ raw = path.read_text(encoding="utf-8")
96
+ data = json.loads(raw)
97
+ if isinstance(data, dict):
98
+ return data
99
+ return {}
100
+ except (OSError, ValueError):
101
+ return {}
102
+
103
+
104
+ def _atomic_write_text(path: Path, content: str) -> None:
105
+ """Crash-safe text write: ``mkstemp`` + ``fsync`` + ``os.replace``.
106
+
107
+ A truncating ``open(path, "w")`` can leave a partial/empty file if the
108
+ process is interrupted mid-write; the atomic temp-file + rename sequence
109
+ guarantees the cache is either the previous contents or the new contents,
110
+ never a torn write.
111
+ """
112
+ path.parent.mkdir(parents=True, exist_ok=True)
113
+ base_dir = str(path.parent)
114
+ fd, tmp_path = tempfile.mkstemp(dir=base_dir, prefix=".vc-atomic-", suffix=".tmp")
115
+ try:
116
+ with os.fdopen(fd, "w", encoding="utf-8") as f:
117
+ f.write(content)
118
+ f.flush()
119
+ os.fsync(f.fileno())
120
+ os.replace(tmp_path, path)
121
+ except BaseException:
122
+ # Best-effort cleanup of the temp file on any failure path.
123
+ try:
124
+ os.unlink(tmp_path)
125
+ except OSError:
126
+ pass
127
+ raise
128
+
129
+
130
+ def _write_cache(data: dict[str, Any]) -> None:
131
+ """Persist the cache atomally (errors swallowed — fail-open)."""
132
+ try:
133
+ _atomic_write_text(_cache_path(), json.dumps(data))
134
+ except OSError:
135
+ pass
136
+
137
+
138
+ # ─── version comparison ──────────────────────────────────────────────────────
139
+ def is_newer(latest: str, current: str) -> bool:
140
+ """Return True iff ``latest`` is strictly newer than ``current``.
141
+
142
+ Uses ``packaging.version`` (bundled with pip/setuptools, so virtually always
143
+ present). Falls back to a conservative inequality check so a missing
144
+ ``packaging`` never breaks the feature.
145
+ """
146
+ try:
147
+ from packaging.version import InvalidVersion
148
+ from packaging.version import parse as _parse
149
+
150
+ try:
151
+ return _parse(latest) > _parse(current)
152
+ except InvalidVersion:
153
+ return False
154
+ except Exception:
155
+ return bool(latest) and bool(current) and latest != current
156
+
157
+
158
+ # ─── network fetch (pluggable for tests) ─────────────────────────────────────
159
+ def _default_fetch(url: str, timeout: float) -> Optional[str]:
160
+ """Query the PyPI JSON API and return the latest version string, or None.
161
+
162
+ Uses the project's existing ``httpx`` dependency (sync client). Any error —
163
+ DNS, timeout, non-200, malformed body — yields None so callers can treat
164
+ "no answer" uniformly.
165
+ """
166
+ import httpx
167
+
168
+ try:
169
+ with httpx.Client(timeout=timeout, follow_redirects=True) as client:
170
+ resp = client.get(url)
171
+ resp.raise_for_status()
172
+ data = resp.json()
173
+ except Exception:
174
+ return None
175
+ version = str((data.get("info") or {}).get("version", "")).strip()
176
+ return version or None
177
+
178
+
179
+ # ─── notice formatting ───────────────────────────────────────────────────────
180
+ def _format_notice(latest: str, current: str) -> str:
181
+ return (
182
+ f"[{PACKAGE_NAME}] update available: {latest} (current: {current})\n"
183
+ f" pip install --upgrade {PACKAGE_NAME}"
184
+ )
185
+
186
+
187
+ # ─── handle & entry point ────────────────────────────────────────────────────
188
+ class UpdateCheckHandle:
189
+ """Opaque handle returned by :func:`start_update_check`.
190
+
191
+ Carries the immediately-known (cached) notice and (optionally) the daemon
192
+ thread performing a fresh fetch. ``collect`` lets a caller briefly wait for
193
+ the fetch to finish so a first-ever run (empty cache) can still surface a
194
+ notice this session.
195
+ """
196
+
197
+ __slots__ = ("_current", "_notice", "_thread")
198
+
199
+ def __init__(self, current: str) -> None:
200
+ self._thread: Optional[threading.Thread] = None
201
+ self._notice: Optional[str] = None
202
+ self._current: str = current
203
+
204
+ @property
205
+ def notice(self) -> Optional[str]:
206
+ """Notice string if a newer version is already known (cached); else None."""
207
+ return self._notice
208
+
209
+ def collect(self, wait_s: float = 0.0) -> Optional[str]:
210
+ """Optionally wait for the background fetch, then return the notice.
211
+
212
+ ``wait_s=0`` is fully non-blocking: it returns the cached notice
213
+ immediately. If a fetch thread is alive and ``wait_s>0`` is given, this
214
+ waits up to ``wait_s`` seconds; once the thread has finished it re-reads
215
+ the cache so a freshly-discovered version is reflected. Never raises.
216
+ """
217
+ thread = self._thread
218
+ if thread is not None and thread.is_alive() and wait_s > 0:
219
+ thread.join(wait_s)
220
+ if thread is not None and not thread.is_alive():
221
+ # Thread finished (now or just above): pick up any newer cached value.
222
+ try:
223
+ latest = _read_cache().get("latest")
224
+ except Exception:
225
+ latest = None
226
+ if latest and is_newer(str(latest), self._current):
227
+ self._notice = _format_notice(str(latest), self._current)
228
+ return self._notice
229
+
230
+
231
+ def _bg_fetch(current: str, fetcher: Callable[[str, float], Optional[str]]) -> None:
232
+ """Daemon-thread body: fetch latest, refresh cache. All errors swallowed."""
233
+ try:
234
+ latest = fetcher(_PYPI_URL, _FETCH_TIMEOUT_S)
235
+ except Exception:
236
+ latest = None
237
+ if latest:
238
+ _write_cache(
239
+ {
240
+ "last_check_ts": time.time(),
241
+ "latest": latest,
242
+ "current_at_check": current,
243
+ }
244
+ )
245
+
246
+
247
+ def start_update_check(
248
+ *,
249
+ interval_hours: float = _DEFAULT_INTERVAL_HOURS,
250
+ current_version: Optional[str] = None,
251
+ fetcher: Optional[Callable[[str, float], Optional[str]]] = None,
252
+ now_ts: Optional[float] = None,
253
+ ) -> UpdateCheckHandle:
254
+ """Start a (possibly skipped) background update check; return a handle.
255
+
256
+ Behaviour:
257
+ * The cache is read first; if it records a *latest* newer than the running
258
+ version, the handle's ``notice`` is populated **immediately** (no network).
259
+ * If the last successful check is older than ``interval_hours`` (or there is
260
+ no cache yet), a daemon thread performs a fresh fetch and refreshes the
261
+ cache for subsequent runs. The fetcher defaults to :func:`_default_fetch`
262
+ but is injectable for tests.
263
+
264
+ This function never raises — a failure here must not impair the CLI.
265
+ """
266
+ current = current_version if current_version is not None else get_current_version()
267
+ handle = UpdateCheckHandle(current)
268
+
269
+ # Uninstalled source checkout: there is no pip-managed install to upgrade,
270
+ # so a notice would be actionable-looking but wrong ("current: 0.0.0").
271
+ # Skip both the notice and the fetch entirely.
272
+ if current == "0.0.0":
273
+ return handle
274
+
275
+ try:
276
+ cache = _read_cache()
277
+ cached_latest = cache.get("latest")
278
+ if cached_latest and is_newer(str(cached_latest), current):
279
+ handle._notice = _format_notice(str(cached_latest), current)
280
+
281
+ last_check = float(cache.get("last_check_ts") or 0)
282
+ now = now_ts if now_ts is not None else time.time()
283
+ interval_s = max(0.0, interval_hours) * 3600.0
284
+ if now - last_check >= interval_s:
285
+ thread = threading.Thread(
286
+ target=_bg_fetch,
287
+ args=(current, fetcher or _default_fetch),
288
+ name=f"{PACKAGE_NAME}-version-check",
289
+ daemon=True,
290
+ )
291
+ handle._thread = thread
292
+ thread.start()
293
+ except Exception:
294
+ # Fail-open: any hiccup just yields a handle with no notice.
295
+ pass
296
+
297
+ return handle
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes