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.
- {asicode-0.2.6/asicode.egg-info → asicode-0.2.7}/PKG-INFO +1 -1
- {asicode-0.2.6 → asicode-0.2.7/asicode.egg-info}/PKG-INFO +1 -1
- {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/SOURCES.txt +3 -1
- asicode-0.2.7/external_llm/editor/_editor_core/common/strategy_priors.py +67 -0
- {asicode-0.2.6 → asicode-0.2.7}/pyproject.toml +1 -1
- asicode-0.2.7/utils/version_check.py +297 -0
- {asicode-0.2.6 → asicode-0.2.7}/LICENSE +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/README.md +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/asi.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/dependency_links.txt +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/entry_points.txt +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/requires.txt +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/asicode.egg-info/top_level.txt +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/common.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/config.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/context_collector.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/diff_apply.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/_shared_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/_thread_pool.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/_user_intent.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_context_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_escalation_pipeline.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_fast_path.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_loop.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_loop_types.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_phase_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_planner_pipeline.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_profile.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/agent_turn_pipeline.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/alignment_scorer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/anchor_shared.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/argument_repairer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/ast_cache.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/ast_engine.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/async_tool_executor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/auto_correction.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/background_job_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/call_graph.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/checkpoint_store.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/config/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/config/thresholds.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_budget.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_contract.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/context_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/design_chat_loop.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/enums.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/execution_mode_classifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/execution_spec.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/execution_thresholds.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/failure_classifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/failure_context.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/failure_pattern_store.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/file_cache.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/fix_spec_claim_validator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/gsg_safety.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/guard_ir.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/handoff_observer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/insights_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/intent_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/intent_resolver.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/interrupt_tool_results.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/json_repair.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/language_backend.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/language_hint.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/learned_policy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/light_repo_index.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/lint_runner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/llm_output_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/local_assistant.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/message_shapes.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/metadata_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/operation_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/orchestrator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/output_normalizer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/performance_metrics.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/placement_contract.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/plan_state.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/planner_lane_facade.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/rag_configs.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/rag_searcher.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/reasoning_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/repair_helpers.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/request_intent_classifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/routing_policy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/run_store.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/scanner_registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/selector_constants.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/semantic_intent.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/semantic_lint.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/session_state.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/slash_commands.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/subagent_ipc.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_engine.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_index.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_locator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_modify_tool.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/symbol_search.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/task_router.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/terminal_coordination.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/termination_policy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/test_impact_selector.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/test_runner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_chain.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_dependency_graph.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_failure_log.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/agent_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/analysis_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/ast_op_executor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/browser_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/constants.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/git_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/read_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/shell_policy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/test_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/web_search_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_handlers/write_tools.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_result_cache.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_safety.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/tool_schemas.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/vector_cache.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/weight_learning.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/agent/work_state_digest.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/_dead_block_shared.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/ast_similarity_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/broken_contract_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/container_reachability_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/contradictory_logic_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/cross_file_refs.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/dead_block_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/duplicate_definition_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/parse_cache.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/public_dead_code_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/unused_import_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/analysis/vulture_scanner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/anthropic_client.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/ast_rewrite.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/client.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/code_analyzer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/code_structure_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/atomic_io.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/file_lock.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/common/indent_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/context/context_packs.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/context_builder.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/dependency_graph.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/design_session.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/dataflow_extractor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/graph_propagator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/relevance_scorer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/edit_localization/request_analyzer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/common/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/common/import_normalizer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/ast_rewriter.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/rollback_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/verifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/execution_vm/vm.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/learning/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/learning/exploration_policy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/primitives/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/primitives/executor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/primitives/models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/failure_classifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/repair_planner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/repair_registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/ts_vm/repair/repair_strategies.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/ast_rewriter.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/classification.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/contract_diff.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/contract_extractor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/contract_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/project_graph.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/contracts/propagation_planner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/failure_classifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/repair_planner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/repair_registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/repair_strategies.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/rollback_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/verifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/_editor_core/vm/vm.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/proactive_runner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/push_manager.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/task_queue.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/trigger_engine.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/autonomous/trigger_policy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/mcp/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/mcp/server.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/prompts/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/agent/prompts/edit_prompts.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/strategy_abstraction.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/cross_language/transfer_engine.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/experience_store.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/exploration_state.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/learning_sink.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/pattern_extractor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_scorer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_store.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_learning_updater.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_scorer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_store.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/primitive_sequence_updater.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/problem_signature.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/strategy_execution_bias.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/strategy_prioritizer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/strategy_state.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/unified_run_record.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/learning/unified_store.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/code_context.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/delete_node.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/executor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/insert_import.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/insert_statement.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/rename_symbol.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/replace_function.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/primitives/update_call.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ast_transform_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/contract_replan_strategy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/draft_parser.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/flow_synthesizer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/fragment_generator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/fragment_integrator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/generation_planner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/libcst_transform_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/output_comparator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_code_templates.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_detector.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_ir_builder.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_reconstructor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/primitive_registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contract_evaluator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contract_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contract_registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_contracts.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_corrector.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_gap_analyzer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_rewrite_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_rewrite_planner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_rewriter.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/semantic_tracer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/synthesis_planner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ts_ir_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ts_semantic_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/semantic/ts_semantic_tracer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/dependency_traversal.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/impact_models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/impact_simulator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/simulator/risk_estimator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/code_integrity.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/impact_propagation.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/impact_verification_mapper.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/editor/verification/verification_set_builder.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/composite_risk.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/execution_graph_advisor.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/graph_builder.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/graph_facade.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/repository_graph.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/run_scoped_graph_cache.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/spec_graph_enricher.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/graph/virtual_graph.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/hybrid_parser.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/image_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/intelligent_service.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/base.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/bash_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/capabilities.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/csharp_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/css_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/dependency_checker.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/go_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/html_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/java_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/javascript_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/json_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/kotlin_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/libcst_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/models.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/php_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/python_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/ruby_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/rust_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/swift_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/syntax_validator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/tree_sitter_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/languages/typescript_provider.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/model_registry.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/multi_planner.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/ollama_api.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/openai_client.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/output_modes.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/output_parser.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/patch_engine.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/patch_synthesizer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/project_analyzer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/providers.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/asi_mcp_adapter.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/claude_session.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/cli.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/collaboration_orchestrator.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/streaming_display.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/repl/collaborate/verdict.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/semantic_patch.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/service.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/smart_analyzer.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/super_context_builder.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/testing/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/testing/symbol_aware_test_finder.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/external_llm/testing/test_dependency_graph.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/patch_synth.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/path_security.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/plan_compiler.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/services/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/services/patch_helpers.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/setup.cfg +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_common.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_config.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_context_budget.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_embedding_singleton.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_intent_classifier.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_learned_policy.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_ollama_api.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_path_security.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_progress_printer_inflight.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_real_agent.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_string_helper.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_work_state_digest.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/tests/test_write_safety_repair_path.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/utils/__init__.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/utils/llm_utils.py +0 -0
- {asicode-0.2.6 → asicode-0.2.7}/utils/string_helper.py +0 -0
|
@@ -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
|
+
}
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|