alysis-code 0.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of alysis-code might be problematic. Click here for more details.
- alysis_code/__init__.py +3 -0
- alysis_code/__main__.py +4 -0
- alysis_code/_build_info.py +14 -0
- alysis_code/account_login.py +468 -0
- alysis_code/agent/README.md +35 -0
- alysis_code/agent/__init__.py +11 -0
- alysis_code/agent/acceptance_contract.py +2217 -0
- alysis_code/agent/blast_radius.py +1403 -0
- alysis_code/agent/cache_keepalive.py +227 -0
- alysis_code/agent/completion_certificate.py +366 -0
- alysis_code/agent/completion_gate.py +306 -0
- alysis_code/agent/empty_response_stall.py +403 -0
- alysis_code/agent/errors.py +28 -0
- alysis_code/agent/llm_calls.py +475 -0
- alysis_code/agent/mutation_classification.py +227 -0
- alysis_code/agent/prompt_context.py +2508 -0
- alysis_code/agent/read_ledger.py +253 -0
- alysis_code/agent/regression_baseline.py +642 -0
- alysis_code/agent/reproduction_first.py +610 -0
- alysis_code/agent/sensitive_output.py +629 -0
- alysis_code/agent/session.py +3218 -0
- alysis_code/agent/steering.py +191 -0
- alysis_code/agent/subagent_execution.py +5177 -0
- alysis_code/agent/subagent_workspace.py +666 -0
- alysis_code/agent/tools_assembly.py +4728 -0
- alysis_code/agent/turn/__init__.py +101 -0
- alysis_code/agent/turn/core.py +8483 -0
- alysis_code/agent/turn/events.py +113 -0
- alysis_code/agent/turn/exploration.py +590 -0
- alysis_code/agent/turn/interventions.py +65 -0
- alysis_code/agent/turn/read_cache.py +420 -0
- alysis_code/agent/turn/snapshot.py +179 -0
- alysis_code/agent/turn_contract.py +661 -0
- alysis_code/agent/turn_path.py +129 -0
- alysis_code/agent/verification.py +2885 -0
- alysis_code/agent/verification_commands.py +512 -0
- alysis_code/agent/verification_evidence.py +738 -0
- alysis_code/agent_loop.py +693 -0
- alysis_code/agent_runtimes/__init__.py +51 -0
- alysis_code/agent_runtimes/base.py +114 -0
- alysis_code/agent_runtimes/builtins.py +129 -0
- alysis_code/agent_runtimes/codex_cli.py +664 -0
- alysis_code/agent_runtimes/host.py +263 -0
- alysis_code/agent_runtimes/registry.py +64 -0
- alysis_code/agent_runtimes/service.py +150 -0
- alysis_code/agentbox_client.py +416 -0
- alysis_code/agentbox_integration.py +310 -0
- alysis_code/alysis_cloud.py +152 -0
- alysis_code/approval_scope.py +276 -0
- alysis_code/assets/README.md +33 -0
- alysis_code/assets/__init__.py +126 -0
- alysis_code/assets/asset_read_core.py +281 -0
- alysis_code/assets/budget_allocator.py +456 -0
- alysis_code/assets/comprehender.py +759 -0
- alysis_code/assets/index.py +654 -0
- alysis_code/assets/ingestion.py +275 -0
- alysis_code/assets/legacy_migration.py +413 -0
- alysis_code/assets/models.py +263 -0
- alysis_code/assets/ocr.py +239 -0
- alysis_code/assets/owl/ascii/f-000.txt +13 -0
- alysis_code/assets/owl/ascii/f-001.txt +13 -0
- alysis_code/assets/owl/ascii/f-002.txt +13 -0
- alysis_code/assets/owl/ascii/f-003.txt +13 -0
- alysis_code/assets/owl/ascii/f-004.txt +13 -0
- alysis_code/assets/owl/ascii/f-005.txt +13 -0
- alysis_code/assets/owl/ascii/f-006.txt +13 -0
- alysis_code/assets/owl/ascii/f-007.txt +13 -0
- alysis_code/assets/owl/ascii/f-008.txt +13 -0
- alysis_code/assets/owl/ascii/f-009.txt +13 -0
- alysis_code/assets/owl/ascii/f-010.txt +13 -0
- alysis_code/assets/owl/ascii/f-011.txt +13 -0
- alysis_code/assets/owl/ascii/f-012.txt +13 -0
- alysis_code/assets/owl/ascii/f-013.txt +13 -0
- alysis_code/assets/owl/ascii/f-014.txt +13 -0
- alysis_code/assets/owl/ascii/f-015.txt +13 -0
- alysis_code/assets/owl/ascii/f-016.txt +13 -0
- alysis_code/assets/owl/ascii/f-017.txt +13 -0
- alysis_code/assets/owl/ascii/f-018.txt +13 -0
- alysis_code/assets/owl/ascii/f-019.txt +13 -0
- alysis_code/assets/owl/ascii/f-020.txt +13 -0
- alysis_code/assets/owl/index.html +98 -0
- alysis_code/assets/owl/show-owl.sh +761 -0
- alysis_code/assets/paths.py +49 -0
- alysis_code/assets/plan_binding.py +326 -0
- alysis_code/assets/planner_context.py +466 -0
- alysis_code/assets/planner_tools.py +184 -0
- alysis_code/assets/prompts.py +101 -0
- alysis_code/assets/replanner_context.py +239 -0
- alysis_code/assets/surface.py +521 -0
- alysis_code/assets/untrusted_content.py +48 -0
- alysis_code/assets/usage_logger.py +94 -0
- alysis_code/assets/worker_mirror.py +428 -0
- alysis_code/assets/worker_section.py +303 -0
- alysis_code/assets/worker_tools.py +468 -0
- alysis_code/atomic_io.py +83 -0
- alysis_code/auth_diagnostics.py +272 -0
- alysis_code/background_runner.py +366 -0
- alysis_code/branding.py +270 -0
- alysis_code/budget_policy.py +390 -0
- alysis_code/build_identity.py +465 -0
- alysis_code/builtin_hooks/__init__.py +7 -0
- alysis_code/builtin_hooks/notify_done_windows.py +65 -0
- alysis_code/bwrap_etc.py +76 -0
- alysis_code/cancellation.py +41 -0
- alysis_code/capabilities.py +137 -0
- alysis_code/chatgpt_codex_static_provider.py +133 -0
- alysis_code/cli.py +51 -0
- alysis_code/cli_impl/__init__.py +1 -0
- alysis_code/cli_impl/assets_cli.py +537 -0
- alysis_code/cli_impl/assets_modal.py +412 -0
- alysis_code/cli_impl/chat/__init__.py +156 -0
- alysis_code/cli_impl/chat/commands.py +2616 -0
- alysis_code/cli_impl/chat/loop.py +4508 -0
- alysis_code/cli_impl/chat/mid_turn_policy.py +125 -0
- alysis_code/cli_impl/chat/rendering.py +444 -0
- alysis_code/cli_impl/chat/state.py +124 -0
- alysis_code/cli_impl/chat_resume.py +830 -0
- alysis_code/cli_impl/chat_slash_completer.py +258 -0
- alysis_code/cli_impl/commands/__init__.py +11 -0
- alysis_code/cli_impl/commands/_shared.py +89 -0
- alysis_code/cli_impl/commands/auth.py +623 -0
- alysis_code/cli_impl/commands/chat_resume_helpers.py +1531 -0
- alysis_code/cli_impl/commands/chat_state.py +158 -0
- alysis_code/cli_impl/commands/chat_status.py +1248 -0
- alysis_code/cli_impl/commands/chat_terminal.py +942 -0
- alysis_code/cli_impl/commands/chat_tui_panels.py +1018 -0
- alysis_code/cli_impl/commands/cli_common.py +1223 -0
- alysis_code/cli_impl/commands/cli_surface.py +77 -0
- alysis_code/cli_impl/commands/config.py +131 -0
- alysis_code/cli_impl/commands/conventions.py +85 -0
- alysis_code/cli_impl/commands/execution_helpers.py +350 -0
- alysis_code/cli_impl/commands/extensions.py +401 -0
- alysis_code/cli_impl/commands/forge.py +1282 -0
- alysis_code/cli_impl/commands/forge_asset_view.py +121 -0
- alysis_code/cli_impl/commands/forge_helpers.py +1215 -0
- alysis_code/cli_impl/commands/hooks.py +737 -0
- alysis_code/cli_impl/commands/ide_bridge.py +31 -0
- alysis_code/cli_impl/commands/mcp.py +700 -0
- alysis_code/cli_impl/commands/profile.py +453 -0
- alysis_code/cli_impl/commands/prompt_helpers.py +307 -0
- alysis_code/cli_impl/commands/report.py +88 -0
- alysis_code/cli_impl/commands/root.py +1118 -0
- alysis_code/cli_impl/commands/sandbox.py +184 -0
- alysis_code/cli_impl/commands/server.py +54 -0
- alysis_code/cli_impl/commands/sessions.py +252 -0
- alysis_code/cli_impl/commands/skills.py +404 -0
- alysis_code/cli_impl/commands/startup.py +946 -0
- alysis_code/cli_impl/commands/tools.py +335 -0
- alysis_code/cli_impl/commands/update.py +364 -0
- alysis_code/cli_impl/commands/welcome.py +972 -0
- alysis_code/cli_impl/config_menu.py +3882 -0
- alysis_code/cli_impl/forge.py +4509 -0
- alysis_code/cli_impl/forge_recovery.py +485 -0
- alysis_code/cli_impl/setup_wizard.py +2409 -0
- alysis_code/cli_impl/tui/__init__.py +58 -0
- alysis_code/cli_impl/tui/app.py +4551 -0
- alysis_code/cli_impl/tui/config.py +32 -0
- alysis_code/cli_impl/tui/config_flow.py +2754 -0
- alysis_code/cli_impl/tui/config_overlay.py +566 -0
- alysis_code/cli_impl/tui/content.py +78 -0
- alysis_code/cli_impl/tui/footer.py +218 -0
- alysis_code/cli_impl/tui/forge_status.py +136 -0
- alysis_code/cli_impl/tui/markdown.py +244 -0
- alysis_code/cli_impl/tui/owl.py +109 -0
- alysis_code/cli_impl/tui/plan_meta.py +477 -0
- alysis_code/cli_impl/tui/setup_app.py +519 -0
- alysis_code/cli_impl/tui/setup_flow.py +1622 -0
- alysis_code/cli_impl/tui/state.py +101 -0
- alysis_code/cli_impl/tui/subagent_identity.py +66 -0
- alysis_code/cli_impl/tui/subagent_panel.py +186 -0
- alysis_code/cli_impl/tui/surface.py +796 -0
- alysis_code/cli_impl/tui/transcript.py +514 -0
- alysis_code/cli_impl/tui/update_prompt.py +79 -0
- alysis_code/cli_impl/tui/workspace_guard.py +384 -0
- alysis_code/clipboard.py +172 -0
- alysis_code/code_review.py +1211 -0
- alysis_code/compaction/__init__.py +28 -0
- alysis_code/compaction/conversation_compactor.py +2932 -0
- alysis_code/compaction/importance.py +177 -0
- alysis_code/compaction/settings.py +297 -0
- alysis_code/compaction/tool_output_offload.py +447 -0
- alysis_code/config.py +3509 -0
- alysis_code/conflict_auto_resolver.py +895 -0
- alysis_code/context/__init__.py +1 -0
- alysis_code/context/tool_schema_budgeter.py +220 -0
- alysis_code/crash_diagnostics.py +282 -0
- alysis_code/custom_tools/README.md +34 -0
- alysis_code/custom_tools/__init__.py +43 -0
- alysis_code/custom_tools/discovery.py +903 -0
- alysis_code/custom_tools/runtime.py +1516 -0
- alysis_code/custom_tools/session.py +227 -0
- alysis_code/custom_tools/trust.py +232 -0
- alysis_code/diff_paths.py +113 -0
- alysis_code/direction_change.py +293 -0
- alysis_code/dispatch_timing.py +306 -0
- alysis_code/durable_service_manager.py +1236 -0
- alysis_code/edit_discipline.py +659 -0
- alysis_code/error_text.py +73 -0
- alysis_code/execution_budget.py +411 -0
- alysis_code/execution_context.py +915 -0
- alysis_code/execution_deadline.py +1065 -0
- alysis_code/execution_shared.py +1904 -0
- alysis_code/extensions/README.md +30 -0
- alysis_code/extensions/__init__.py +93 -0
- alysis_code/extensions/activation.py +138 -0
- alysis_code/extensions/install.py +1436 -0
- alysis_code/extensions/manifest.py +487 -0
- alysis_code/extensions/models.py +74 -0
- alysis_code/extensions/paths.py +56 -0
- alysis_code/extensions/registry.json +4 -0
- alysis_code/extensions/registry.py +52 -0
- alysis_code/extensions/state.py +83 -0
- alysis_code/extensions/workspace_trust.py +101 -0
- alysis_code/failed_task_evidence.py +369 -0
- alysis_code/failure_category.py +315 -0
- alysis_code/feedback_report.py +1647 -0
- alysis_code/file_classification.py +485 -0
- alysis_code/forge.py +2064 -0
- alysis_code/forge_completion.py +362 -0
- alysis_code/forge_events.py +475 -0
- alysis_code/frontmatter_utils.py +95 -0
- alysis_code/git_evidence.py +1181 -0
- alysis_code/git_ops.py +560 -0
- alysis_code/git_safe.py +62 -0
- alysis_code/git_worktrees.py +190 -0
- alysis_code/hooks/README.md +33 -0
- alysis_code/hooks/__init__.py +67 -0
- alysis_code/hooks/audit.py +171 -0
- alysis_code/hooks/config.py +225 -0
- alysis_code/hooks/dispatcher.py +1110 -0
- alysis_code/hooks/models.py +447 -0
- alysis_code/hooks/trust.py +202 -0
- alysis_code/host_actions.py +543 -0
- alysis_code/host_browser.py +103 -0
- alysis_code/ide/__init__.py +5 -0
- alysis_code/ide/activity_events.py +399 -0
- alysis_code/ide/approvals.py +337 -0
- alysis_code/ide/artifacts.py +153 -0
- alysis_code/ide/browser_egress_proxy.py +1076 -0
- alysis_code/ide/cdp_websocket_transport.py +1192 -0
- alysis_code/ide/change_ledger.py +1721 -0
- alysis_code/ide/context_blocks.py +979 -0
- alysis_code/ide/event_stream.py +531 -0
- alysis_code/ide/forge_protocol.py +3112 -0
- alysis_code/ide/forge_request_ledger.py +737 -0
- alysis_code/ide/health.py +965 -0
- alysis_code/ide/managed_browser.py +2251 -0
- alysis_code/ide/management_protocol.py +3414 -0
- alysis_code/ide/mcp_oauth_coordinator.py +744 -0
- alysis_code/ide/mcp_oauth_lifecycle.py +1504 -0
- alysis_code/ide/prompt_queue.py +1070 -0
- alysis_code/ide/protocol.py +191 -0
- alysis_code/ide/resumable_swarm.py +1543 -0
- alysis_code/ide/session_search.py +295 -0
- alysis_code/ide/stdio_bridge.py +9935 -0
- alysis_code/ide/structured_state.py +1579 -0
- alysis_code/ide/swarm_protocol.py +816 -0
- alysis_code/integration_gate.py +506 -0
- alysis_code/interactive_input_guard.py +39 -0
- alysis_code/interactive_plan_mode.py +26 -0
- alysis_code/internal_artifacts.py +179 -0
- alysis_code/knowledge_base.py +1409 -0
- alysis_code/knowledge_capture.py +1190 -0
- alysis_code/knowledge_librarian.py +605 -0
- alysis_code/language_policy.py +34 -0
- alysis_code/litellm_static_provider.py +535 -0
- alysis_code/llm/__init__.py +1 -0
- alysis_code/llm/anthropic_messages.py +2288 -0
- alysis_code/llm/base.py +71 -0
- alysis_code/llm/cache_capabilities.py +985 -0
- alysis_code/llm/cache_control_blocks.py +244 -0
- alysis_code/llm/cache_policy.py +388 -0
- alysis_code/llm/factory.py +373 -0
- alysis_code/llm/gemini_generate_content.py +2652 -0
- alysis_code/llm/gemini_interactions.py +739 -0
- alysis_code/llm/metadata.py +450 -0
- alysis_code/llm/openai_compat.py +2947 -0
- alysis_code/llm/openai_responses.py +2604 -0
- alysis_code/llm/protocols.py +609 -0
- alysis_code/llm/provider_limits.py +525 -0
- alysis_code/llm/request_plan.py +389 -0
- alysis_code/llm/request_shape.py +238 -0
- alysis_code/llm/streaming.py +108 -0
- alysis_code/llm/temperature_compat.py +78 -0
- alysis_code/llm/types.py +195 -0
- alysis_code/llm/usage_normalization.py +222 -0
- alysis_code/llm_error_display.py +315 -0
- alysis_code/logging_redaction.py +326 -0
- alysis_code/managed_host_deadline.py +196 -0
- alysis_code/mcp/README.md +33 -0
- alysis_code/mcp/__init__.py +24 -0
- alysis_code/mcp/client.py +1137 -0
- alysis_code/mcp/config.py +597 -0
- alysis_code/mcp/errors.py +113 -0
- alysis_code/mcp/forge_scope.py +154 -0
- alysis_code/mcp/jsonrpc.py +214 -0
- alysis_code/mcp/manager.py +2308 -0
- alysis_code/mcp/models.py +666 -0
- alysis_code/mcp/oauth.py +972 -0
- alysis_code/mcp/oauth_runtime.py +310 -0
- alysis_code/mcp/oauth_store.py +276 -0
- alysis_code/mcp/prompts.py +329 -0
- alysis_code/mcp/resources.py +295 -0
- alysis_code/mcp/roots.py +106 -0
- alysis_code/mcp/server_requests.py +75 -0
- alysis_code/mcp/token_store.py +859 -0
- alysis_code/mcp/transport_http.py +1338 -0
- alysis_code/mcp/transport_stdio.py +1267 -0
- alysis_code/mcp/untrusted_content.py +119 -0
- alysis_code/merge_conflict_reviewer.py +729 -0
- alysis_code/model_catalog/__init__.py +1 -0
- alysis_code/model_catalog/chatgpt_codex_subscription_snapshot.json +186 -0
- alysis_code/model_catalog/litellm_model_prices_snapshot.json +44715 -0
- alysis_code/model_catalog/litellm_model_prices_snapshot.meta.json +17 -0
- alysis_code/model_metadata_policy.py +223 -0
- alysis_code/model_metadata_utils.py +103 -0
- alysis_code/model_registry.py +1420 -0
- alysis_code/model_router.py +147 -0
- alysis_code/permission_policy.py +1016 -0
- alysis_code/personas.py +451 -0
- alysis_code/pipeline_facts.py +233 -0
- alysis_code/plan_assistant.py +4763 -0
- alysis_code/plan_mode.py +393 -0
- alysis_code/plan_reconciliation.py +1228 -0
- alysis_code/plan_repair.py +652 -0
- alysis_code/plan_validation.py +1099 -0
- alysis_code/planning_constraints.py +904 -0
- alysis_code/policy.py +95 -0
- alysis_code/preview_server.py +457 -0
- alysis_code/process_reaping.py +566 -0
- alysis_code/profile_presets.py +1834 -0
- alysis_code/profiles.py +666 -0
- alysis_code/provider_auth/__init__.py +29 -0
- alysis_code/provider_auth/base.py +99 -0
- alysis_code/provider_auth/openai_codex.py +951 -0
- alysis_code/provider_auth/registry.py +76 -0
- alysis_code/provider_auth/store.py +125 -0
- alysis_code/provider_diagnostics.py +1209 -0
- alysis_code/provider_model_catalog.py +685 -0
- alysis_code/provider_telemetry.py +1699 -0
- alysis_code/provider_url.py +75 -0
- alysis_code/reasoning_contracts.py +911 -0
- alysis_code/remote_sync.py +350 -0
- alysis_code/replanning.py +1195 -0
- alysis_code/repo_scan.py +1152 -0
- alysis_code/request_estimation.py +296 -0
- alysis_code/review_gate.py +617 -0
- alysis_code/run_lock.py +1141 -0
- alysis_code/run_outcome.py +58 -0
- alysis_code/run_provenance.py +774 -0
- alysis_code/run_state.py +445 -0
- alysis_code/runtime_artifacts.py +116 -0
- alysis_code/runtime_context_features.py +78 -0
- alysis_code/runtime_kind.py +52 -0
- alysis_code/safety/__init__.py +11 -0
- alysis_code/safety/mcp_sanitize.py +29 -0
- alysis_code/safety/safe_http.py +297 -0
- alysis_code/safety/subagent_report.py +184 -0
- alysis_code/sandbox_doctor.py +682 -0
- alysis_code/sandbox_runner.py +1025 -0
- alysis_code/sandbox_settings.py +423 -0
- alysis_code/serialized_paths.py +355 -0
- alysis_code/server/__init__.py +3 -0
- alysis_code/server/app.py +367 -0
- alysis_code/server/auth.py +34 -0
- alysis_code/server/job_config.py +30 -0
- alysis_code/server/settings.py +215 -0
- alysis_code/server/store.py +193 -0
- alysis_code/server/worker_runner.py +657 -0
- alysis_code/service_persistence.py +355 -0
- alysis_code/session_artifacts.py +108 -0
- alysis_code/session_metrics.py +331 -0
- alysis_code/session_store.py +624 -0
- alysis_code/skills/README.md +34 -0
- alysis_code/skills/__init__.py +104 -0
- alysis_code/skills/conventions.py +84 -0
- alysis_code/skills/discovery.py +176 -0
- alysis_code/skills/eval_models.py +232 -0
- alysis_code/skills/eval_runner.py +372 -0
- alysis_code/skills/evals.py +1344 -0
- alysis_code/skills/install.py +293 -0
- alysis_code/skills/loader.py +118 -0
- alysis_code/skills/matching.py +103 -0
- alysis_code/skills/models.py +71 -0
- alysis_code/skills/paths.py +56 -0
- alysis_code/skills/prompting.py +500 -0
- alysis_code/skills/scaffold.py +142 -0
- alysis_code/skills/state.py +441 -0
- alysis_code/skills/transactions.py +125 -0
- alysis_code/skills/validation.py +304 -0
- alysis_code/step_budget.py +238 -0
- alysis_code/subagent_labels.py +49 -0
- alysis_code/subagents.py +1072 -0
- alysis_code/surface/__init__.py +80 -0
- alysis_code/surface/base.py +305 -0
- alysis_code/surface/console.py +387 -0
- alysis_code/surface/events.py +372 -0
- alysis_code/surface/hidden_surface.py +529 -0
- alysis_code/surface/noop_surface.py +219 -0
- alysis_code/surface/rich_surface.py +1555 -0
- alysis_code/surface/styles.py +67 -0
- alysis_code/surface/theme.py +455 -0
- alysis_code/surface/types.py +100 -0
- alysis_code/swarm_backend.py +926 -0
- alysis_code/swarm_orchestrator.py +4020 -0
- alysis_code/swarm_scheduler.py +441 -0
- alysis_code/swarm_trace.py +429 -0
- alysis_code/swarm_worker.py +2119 -0
- alysis_code/swarm_write_guard.py +348 -0
- alysis_code/task_dependencies.py +170 -0
- alysis_code/task_readiness.py +992 -0
- alysis_code/task_scope.py +2148 -0
- alysis_code/terminal_manager.py +762 -0
- alysis_code/terminal_ownership.py +460 -0
- alysis_code/text_normalization.py +30 -0
- alysis_code/token_budget.py +97 -0
- alysis_code/tools/README.md +34 -0
- alysis_code/tools/__init__.py +1 -0
- alysis_code/tools/artifacts.py +127 -0
- alysis_code/tools/availability.py +188 -0
- alysis_code/tools/fs.py +1456 -0
- alysis_code/tools/git.py +461 -0
- alysis_code/tools/history.py +229 -0
- alysis_code/tools/http_timeout.py +78 -0
- alysis_code/tools/image_generation.py +552 -0
- alysis_code/tools/registry.py +2936 -0
- alysis_code/tools/repo_map.py +476 -0
- alysis_code/tools/search.py +563 -0
- alysis_code/tools/shell.py +135 -0
- alysis_code/tools/symbols.py +1350 -0
- alysis_code/tools/test_discovery.py +643 -0
- alysis_code/tools/web.py +482 -0
- alysis_code/tools/web_search.py +2012 -0
- alysis_code/tools/web_search_dashscope.py +557 -0
- alysis_code/tools/web_search_ddgs.py +221 -0
- alysis_code/tools/web_search_provider_adapters.py +1429 -0
- alysis_code/tools/web_search_tavily.py +194 -0
- alysis_code/updates.py +933 -0
- alysis_code/usage_tracker.py +1990 -0
- alysis_code/verification_command_analysis.py +1004 -0
- alysis_code/verification_contract.py +574 -0
- alysis_code/verification_failure_summary.py +273 -0
- alysis_code/verification_repair.py +385 -0
- alysis_code/verify_gate.py +3129 -0
- alysis_code/web_research.py +1872 -0
- alysis_code/web_search_adapters.py +66 -0
- alysis_code/web_search_policy.py +27 -0
- alysis_code/workspace_binding.py +389 -0
- alysis_code/workspace_binding_ui.py +408 -0
- alysis_code/workspace_context.py +273 -0
- alysis_code/workspace_isolation.py +138 -0
- alysis_code/workspace_provisioning.py +455 -0
- alysis_code-0.13.0.dist-info/METADATA +507 -0
- alysis_code-0.13.0.dist-info/RECORD +458 -0
- alysis_code-0.13.0.dist-info/WHEEL +4 -0
- alysis_code-0.13.0.dist-info/entry_points.txt +3 -0
- alysis_code-0.13.0.dist-info/licenses/LICENSE +176 -0
- alysis_code-0.13.0.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Semantic Rich styles for terminal UI rendering."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
TerminalTheme = Literal["light", "dark", "neutral"]
|
|
8
|
+
|
|
9
|
+
STYLE_EMPHASIS = "bold"
|
|
10
|
+
STYLE_CONTENT = ""
|
|
11
|
+
STYLE_DIM = "dim"
|
|
12
|
+
STYLE_CHROME = "bright_black"
|
|
13
|
+
STYLE_SELECTED_LABEL = "bold"
|
|
14
|
+
STYLE_SELECTED_DESC = "bold"
|
|
15
|
+
STYLE_DESELECTED_LABEL = "dim"
|
|
16
|
+
STYLE_DESELECTED_DESC = "dim"
|
|
17
|
+
STYLE_ACCENT = "cyan"
|
|
18
|
+
STYLE_WARN = "yellow"
|
|
19
|
+
STYLE_ERROR = "red"
|
|
20
|
+
STYLE_SUCCESS = "green"
|
|
21
|
+
STYLE_SUBAGENT = "bright_cyan"
|
|
22
|
+
|
|
23
|
+
_BASE_STYLES: dict[str, str] = {
|
|
24
|
+
"STYLE_EMPHASIS": STYLE_EMPHASIS,
|
|
25
|
+
"STYLE_CONTENT": STYLE_CONTENT,
|
|
26
|
+
"STYLE_DIM": STYLE_DIM,
|
|
27
|
+
"STYLE_CHROME": STYLE_CHROME,
|
|
28
|
+
"STYLE_SELECTED_LABEL": STYLE_SELECTED_LABEL,
|
|
29
|
+
"STYLE_SELECTED_DESC": STYLE_SELECTED_DESC,
|
|
30
|
+
"STYLE_DESELECTED_LABEL": STYLE_DESELECTED_LABEL,
|
|
31
|
+
"STYLE_DESELECTED_DESC": STYLE_DESELECTED_DESC,
|
|
32
|
+
"STYLE_WARN": STYLE_WARN,
|
|
33
|
+
"STYLE_ERROR": STYLE_ERROR,
|
|
34
|
+
"STYLE_SUCCESS": STYLE_SUCCESS,
|
|
35
|
+
"STYLE_SUBAGENT": STYLE_SUBAGENT,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def theme_aware_styles(theme: TerminalTheme) -> dict[str, str]:
|
|
40
|
+
"""Return semantic styles whose accents are tuned for the detected theme."""
|
|
41
|
+
styles = dict(_BASE_STYLES)
|
|
42
|
+
if theme == "light":
|
|
43
|
+
styles["STYLE_ACCENT"] = "blue"
|
|
44
|
+
elif theme == "dark":
|
|
45
|
+
styles["STYLE_ACCENT"] = STYLE_ACCENT
|
|
46
|
+
else:
|
|
47
|
+
styles["STYLE_ACCENT"] = ""
|
|
48
|
+
return styles
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"STYLE_ACCENT",
|
|
53
|
+
"STYLE_CHROME",
|
|
54
|
+
"STYLE_CONTENT",
|
|
55
|
+
"STYLE_DESELECTED_DESC",
|
|
56
|
+
"STYLE_DESELECTED_LABEL",
|
|
57
|
+
"STYLE_DIM",
|
|
58
|
+
"STYLE_EMPHASIS",
|
|
59
|
+
"STYLE_ERROR",
|
|
60
|
+
"STYLE_SELECTED_DESC",
|
|
61
|
+
"STYLE_SELECTED_LABEL",
|
|
62
|
+
"STYLE_SUBAGENT",
|
|
63
|
+
"STYLE_SUCCESS",
|
|
64
|
+
"STYLE_WARN",
|
|
65
|
+
"TerminalTheme",
|
|
66
|
+
"theme_aware_styles",
|
|
67
|
+
]
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
"""Terminal background theme detection shared by Rich and owl renderers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import select
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from ..branding import env_get
|
|
16
|
+
from .styles import TerminalTheme
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _stream_is_tty(stream: Any | None) -> bool:
|
|
20
|
+
isatty = getattr(stream, "isatty", None)
|
|
21
|
+
if not callable(isatty):
|
|
22
|
+
return False
|
|
23
|
+
try:
|
|
24
|
+
return bool(isatty())
|
|
25
|
+
except Exception:
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _theme_debug(message: str) -> None:
|
|
30
|
+
if env_get("ALYSIS_THEME_DEBUG"):
|
|
31
|
+
print(f"[alysis-theme] {message}", file=sys.stderr)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _truthy_env(name: str) -> bool:
|
|
35
|
+
return str(env_get(name) or "").strip().lower() in {"1", "true", "yes", "on"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _is_wsl() -> bool:
|
|
39
|
+
if os.name == "nt":
|
|
40
|
+
return False
|
|
41
|
+
if os.environ.get("WSL_DISTRO_NAME") or os.environ.get("WSL_INTEROP"):
|
|
42
|
+
return True
|
|
43
|
+
try:
|
|
44
|
+
with open("/proc/version", encoding="utf-8") as fh:
|
|
45
|
+
return "microsoft" in fh.read().lower()
|
|
46
|
+
except OSError:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _normalize_theme(value: str | None) -> TerminalTheme | None:
|
|
51
|
+
raw = str(value or "").strip().lower()
|
|
52
|
+
if raw in {"light", "dark", "neutral"}:
|
|
53
|
+
return raw # type: ignore[return-value]
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _theme_from_colorfgbg(value: str | None) -> TerminalTheme | None:
|
|
58
|
+
if not value:
|
|
59
|
+
return None
|
|
60
|
+
bg = value.rsplit(";", 1)[-1].strip()
|
|
61
|
+
if not bg.isdigit():
|
|
62
|
+
return None
|
|
63
|
+
bg_code = int(bg)
|
|
64
|
+
return "dark" if bg_code <= 6 or bg_code == 8 else "light"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _hex_to_8bit(value: str) -> int:
|
|
68
|
+
max_value = (16 ** len(value)) - 1
|
|
69
|
+
if max_value <= 0:
|
|
70
|
+
return 0
|
|
71
|
+
return int(value, 16) * 255 // max_value
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _theme_from_osc11(stream: Any | None) -> TerminalTheme | None:
|
|
75
|
+
# OSC 11 is unreliable through Windows ConPTY/WSL: replies can arrive
|
|
76
|
+
# after our timeout and leak into the next /dev/tty reader.
|
|
77
|
+
if _is_wsl():
|
|
78
|
+
_theme_debug("OSC11=skipped (WSL)")
|
|
79
|
+
return None
|
|
80
|
+
if not _stream_is_tty(stream):
|
|
81
|
+
_theme_debug("OSC11=skipped (stream is not a TTY)")
|
|
82
|
+
return None
|
|
83
|
+
try:
|
|
84
|
+
import termios
|
|
85
|
+
import tty
|
|
86
|
+
|
|
87
|
+
tty_fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY)
|
|
88
|
+
except Exception:
|
|
89
|
+
return None
|
|
90
|
+
old_attrs = None
|
|
91
|
+
response = ""
|
|
92
|
+
try:
|
|
93
|
+
old_attrs = termios.tcgetattr(tty_fd)
|
|
94
|
+
tty.setraw(tty_fd, termios.TCSANOW)
|
|
95
|
+
os.write(tty_fd, b"\033]11;?\007")
|
|
96
|
+
ready, _, _ = select.select([tty_fd], [], [], 0.5)
|
|
97
|
+
if not ready:
|
|
98
|
+
_theme_debug("OSC11=timeout -> none")
|
|
99
|
+
return None
|
|
100
|
+
response = os.read(tty_fd, 128).decode("ascii", errors="ignore")
|
|
101
|
+
except Exception:
|
|
102
|
+
return None
|
|
103
|
+
finally:
|
|
104
|
+
# Best-effort drain: consume trailing terminal response bytes before
|
|
105
|
+
# prompt_toolkit or another /dev/tty reader can see them as input.
|
|
106
|
+
with contextlib.suppress(Exception):
|
|
107
|
+
os.set_blocking(tty_fd, False)
|
|
108
|
+
deadline = time.monotonic() + 0.025
|
|
109
|
+
while True:
|
|
110
|
+
timeout = max(0.0, deadline - time.monotonic())
|
|
111
|
+
if timeout <= 0:
|
|
112
|
+
break
|
|
113
|
+
ready, _, _ = select.select([tty_fd], [], [], min(timeout, 0.005))
|
|
114
|
+
if not ready:
|
|
115
|
+
continue
|
|
116
|
+
if not os.read(tty_fd, 4096):
|
|
117
|
+
break
|
|
118
|
+
if old_attrs is not None:
|
|
119
|
+
with contextlib.suppress(Exception):
|
|
120
|
+
termios.tcsetattr(tty_fd, termios.TCSANOW, old_attrs)
|
|
121
|
+
with contextlib.suppress(Exception):
|
|
122
|
+
os.close(tty_fd)
|
|
123
|
+
|
|
124
|
+
match = re.search(r"rgb:([0-9A-Fa-f]+)/([0-9A-Fa-f]+)/([0-9A-Fa-f]+)", response)
|
|
125
|
+
if not match:
|
|
126
|
+
_theme_debug(f"OSC11={response!r} -> none")
|
|
127
|
+
return None
|
|
128
|
+
red = _hex_to_8bit(match.group(1))
|
|
129
|
+
green = _hex_to_8bit(match.group(2))
|
|
130
|
+
blue = _hex_to_8bit(match.group(3))
|
|
131
|
+
brightness = (red * 299 + green * 587 + blue * 114) // 1000
|
|
132
|
+
detected: TerminalTheme = "light" if brightness >= 128 else "dark"
|
|
133
|
+
_theme_debug(f"OSC11=rgb:{match.group(1)}/{match.group(2)}/{match.group(3)} -> {detected}")
|
|
134
|
+
return detected
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _strip_json_comments(value: str) -> str:
|
|
138
|
+
output: list[str] = []
|
|
139
|
+
in_string = False
|
|
140
|
+
escaped = False
|
|
141
|
+
in_line_comment = False
|
|
142
|
+
in_block_comment = False
|
|
143
|
+
index = 0
|
|
144
|
+
while index < len(value):
|
|
145
|
+
char = value[index]
|
|
146
|
+
next_char = value[index + 1] if index + 1 < len(value) else ""
|
|
147
|
+
if in_line_comment:
|
|
148
|
+
if char == "\n":
|
|
149
|
+
in_line_comment = False
|
|
150
|
+
output.append(char)
|
|
151
|
+
index += 1
|
|
152
|
+
continue
|
|
153
|
+
if in_block_comment:
|
|
154
|
+
if char == "*" and next_char == "/":
|
|
155
|
+
in_block_comment = False
|
|
156
|
+
index += 2
|
|
157
|
+
else:
|
|
158
|
+
index += 1
|
|
159
|
+
continue
|
|
160
|
+
if in_string:
|
|
161
|
+
output.append(char)
|
|
162
|
+
if escaped:
|
|
163
|
+
escaped = False
|
|
164
|
+
elif char == "\\":
|
|
165
|
+
escaped = True
|
|
166
|
+
elif char == '"':
|
|
167
|
+
in_string = False
|
|
168
|
+
index += 1
|
|
169
|
+
continue
|
|
170
|
+
if char == '"':
|
|
171
|
+
in_string = True
|
|
172
|
+
output.append(char)
|
|
173
|
+
index += 1
|
|
174
|
+
continue
|
|
175
|
+
if char == "/" and next_char == "/":
|
|
176
|
+
in_line_comment = True
|
|
177
|
+
index += 2
|
|
178
|
+
continue
|
|
179
|
+
if char == "/" and next_char == "*":
|
|
180
|
+
in_block_comment = True
|
|
181
|
+
index += 2
|
|
182
|
+
continue
|
|
183
|
+
output.append(char)
|
|
184
|
+
index += 1
|
|
185
|
+
return re.sub(r",(\s*[}\]])", r"\1", "".join(output))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _safe_is_dir(path: Path) -> bool:
|
|
189
|
+
with contextlib.suppress(OSError):
|
|
190
|
+
return path.is_dir()
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _safe_is_file(path: Path) -> bool:
|
|
195
|
+
with contextlib.suppress(OSError):
|
|
196
|
+
return path.is_file()
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _safe_sorted_glob(path: Path, pattern: str) -> list[Path]:
|
|
201
|
+
with contextlib.suppress(OSError):
|
|
202
|
+
return sorted(path.glob(pattern))
|
|
203
|
+
return []
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _safe_sorted_iterdir(path: Path) -> list[Path]:
|
|
207
|
+
with contextlib.suppress(OSError):
|
|
208
|
+
return sorted(path.iterdir())
|
|
209
|
+
return []
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _windows_terminal_settings_paths() -> list[Path]:
|
|
213
|
+
override = env_get("ALYSIS_WINDOWS_TERMINAL_SETTINGS")
|
|
214
|
+
if override:
|
|
215
|
+
return [Path(override).expanduser()]
|
|
216
|
+
|
|
217
|
+
paths: list[Path] = []
|
|
218
|
+
drive_roots = [
|
|
219
|
+
*_safe_sorted_glob(Path("/mnt"), "[a-z]"),
|
|
220
|
+
*_safe_sorted_glob(Path("/mnt/host"), "[a-z]"),
|
|
221
|
+
]
|
|
222
|
+
for drive_root in dict.fromkeys(drive_roots):
|
|
223
|
+
users_dir = drive_root / "Users"
|
|
224
|
+
if not _safe_is_dir(users_dir):
|
|
225
|
+
continue
|
|
226
|
+
for user_dir in _safe_sorted_iterdir(users_dir):
|
|
227
|
+
local_app_data = user_dir / "AppData" / "Local"
|
|
228
|
+
packages_dir = local_app_data / "Packages"
|
|
229
|
+
paths.extend(
|
|
230
|
+
_safe_sorted_glob(
|
|
231
|
+
packages_dir,
|
|
232
|
+
"Microsoft.WindowsTerminal*_8wekyb3d8bbwe/LocalState/settings.json",
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
paths.append(local_app_data / "Microsoft" / "Windows Terminal" / "settings.json")
|
|
236
|
+
return paths
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _load_jsonc_file(path: Path) -> dict[str, Any] | None:
|
|
240
|
+
try:
|
|
241
|
+
return json.loads(_strip_json_comments(path.read_text(encoding="utf-8")))
|
|
242
|
+
except Exception:
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _theme_from_hex_background(value: Any) -> TerminalTheme | None:
|
|
247
|
+
if not isinstance(value, str):
|
|
248
|
+
return None
|
|
249
|
+
raw = value.strip().lstrip("#")
|
|
250
|
+
if len(raw) == 3:
|
|
251
|
+
raw = "".join(char * 2 for char in raw)
|
|
252
|
+
if len(raw) != 6 or re.search(r"[^0-9A-Fa-f]", raw):
|
|
253
|
+
return None
|
|
254
|
+
red = int(raw[0:2], 16)
|
|
255
|
+
green = int(raw[2:4], 16)
|
|
256
|
+
blue = int(raw[4:6], 16)
|
|
257
|
+
brightness = (red * 299 + green * 587 + blue * 114) // 1000
|
|
258
|
+
return "light" if brightness >= 128 else "dark"
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _windows_terminal_builtin_scheme_theme(name: str) -> TerminalTheme | None:
|
|
262
|
+
normalized = re.sub(r"[^a-z0-9+]+", " ", name.lower()).strip()
|
|
263
|
+
normalized = re.sub(r"\s+", " ", normalized)
|
|
264
|
+
known: dict[str, TerminalTheme] = {
|
|
265
|
+
"atom one dark": "dark",
|
|
266
|
+
"atom one light": "light",
|
|
267
|
+
"ayu dark": "dark",
|
|
268
|
+
"ayu light": "light",
|
|
269
|
+
"ayu mirage": "dark",
|
|
270
|
+
"campbell": "dark",
|
|
271
|
+
"campbell powershell": "dark",
|
|
272
|
+
"dark+": "dark",
|
|
273
|
+
"dracula": "dark",
|
|
274
|
+
"dracula+": "dark",
|
|
275
|
+
"github dark": "dark",
|
|
276
|
+
"github light": "light",
|
|
277
|
+
"gruvbox dark": "dark",
|
|
278
|
+
"gruvbox light": "light",
|
|
279
|
+
"light+": "light",
|
|
280
|
+
"material dark": "dark",
|
|
281
|
+
"material light": "light",
|
|
282
|
+
"monokai": "dark",
|
|
283
|
+
"monokai pro": "dark",
|
|
284
|
+
"nord": "dark",
|
|
285
|
+
"one half dark": "dark",
|
|
286
|
+
"one half light": "light",
|
|
287
|
+
"powershell": "dark",
|
|
288
|
+
"solarized dark": "dark",
|
|
289
|
+
"solarized light": "light",
|
|
290
|
+
"tango dark": "dark",
|
|
291
|
+
"tango light": "light",
|
|
292
|
+
"tokyo night": "dark",
|
|
293
|
+
"tokyo night light": "light",
|
|
294
|
+
"tokyo night storm": "dark",
|
|
295
|
+
"ubuntu": "dark",
|
|
296
|
+
"vintage": "dark",
|
|
297
|
+
}
|
|
298
|
+
return known.get(normalized)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _windows_terminal_scheme_name(settings: dict[str, Any]) -> str | None:
|
|
302
|
+
profiles = settings.get("profiles")
|
|
303
|
+
profiles = profiles if isinstance(profiles, dict) else {}
|
|
304
|
+
profile_list = profiles.get("list")
|
|
305
|
+
profile_list = profile_list if isinstance(profile_list, list) else []
|
|
306
|
+
profile_defaults = profiles.get("defaults")
|
|
307
|
+
profile_defaults = profile_defaults if isinstance(profile_defaults, dict) else {}
|
|
308
|
+
profile_id = str(os.environ.get("WT_PROFILE_ID") or "").strip().strip("{}").lower()
|
|
309
|
+
|
|
310
|
+
profile: dict[str, Any] | None = None
|
|
311
|
+
if profile_id:
|
|
312
|
+
for candidate in profile_list:
|
|
313
|
+
if not isinstance(candidate, dict):
|
|
314
|
+
continue
|
|
315
|
+
guid = str(candidate.get("guid") or "").strip().strip("{}").lower()
|
|
316
|
+
if guid == profile_id:
|
|
317
|
+
profile = candidate
|
|
318
|
+
break
|
|
319
|
+
if profile is None:
|
|
320
|
+
default_profile = str(settings.get("defaultProfile") or "").strip().strip("{}").lower()
|
|
321
|
+
for candidate in profile_list:
|
|
322
|
+
if not isinstance(candidate, dict):
|
|
323
|
+
continue
|
|
324
|
+
guid = str(candidate.get("guid") or "").strip().strip("{}").lower()
|
|
325
|
+
if guid == default_profile:
|
|
326
|
+
profile = candidate
|
|
327
|
+
break
|
|
328
|
+
|
|
329
|
+
scheme = (profile or {}).get("colorScheme")
|
|
330
|
+
if isinstance(scheme, str) and scheme.strip():
|
|
331
|
+
return scheme.strip()
|
|
332
|
+
scheme = profile_defaults.get("colorScheme")
|
|
333
|
+
if isinstance(scheme, str) and scheme.strip():
|
|
334
|
+
return scheme.strip()
|
|
335
|
+
return "Campbell"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _theme_from_windows_terminal_settings() -> TerminalTheme | None:
|
|
339
|
+
has_windows_terminal = bool(os.environ.get("WT_SESSION"))
|
|
340
|
+
override = env_get("ALYSIS_WINDOWS_TERMINAL_SETTINGS")
|
|
341
|
+
is_wsl = _is_wsl()
|
|
342
|
+
_theme_debug(f"is_wsl={is_wsl}, WT_SESSION={'present' if has_windows_terminal else 'missing'}")
|
|
343
|
+
if not has_windows_terminal and not override and not is_wsl:
|
|
344
|
+
return None
|
|
345
|
+
try:
|
|
346
|
+
settings_paths = _windows_terminal_settings_paths()
|
|
347
|
+
except OSError as exc:
|
|
348
|
+
_theme_debug(f"settings.json paths failed: {exc}")
|
|
349
|
+
return "dark" if has_windows_terminal else None
|
|
350
|
+
_theme_debug(f"settings.json paths tried: {len(settings_paths)}")
|
|
351
|
+
for settings_path in settings_paths:
|
|
352
|
+
if not _safe_is_file(settings_path):
|
|
353
|
+
continue
|
|
354
|
+
settings = _load_jsonc_file(settings_path)
|
|
355
|
+
if not settings:
|
|
356
|
+
continue
|
|
357
|
+
scheme_name = _windows_terminal_scheme_name(settings)
|
|
358
|
+
if not scheme_name:
|
|
359
|
+
continue
|
|
360
|
+
schemes = settings.get("schemes")
|
|
361
|
+
schemes = schemes if isinstance(schemes, list) else []
|
|
362
|
+
for scheme in schemes:
|
|
363
|
+
if not isinstance(scheme, dict):
|
|
364
|
+
continue
|
|
365
|
+
name = str(scheme.get("name") or "").strip()
|
|
366
|
+
if name.lower() != scheme_name.lower():
|
|
367
|
+
continue
|
|
368
|
+
detected = _theme_from_hex_background(scheme.get("background"))
|
|
369
|
+
if detected:
|
|
370
|
+
_theme_debug(f'active scheme="{scheme_name}" -> {detected}')
|
|
371
|
+
return detected
|
|
372
|
+
detected = _windows_terminal_builtin_scheme_theme(scheme_name)
|
|
373
|
+
if detected:
|
|
374
|
+
_theme_debug(f'active scheme="{scheme_name}" -> {detected}')
|
|
375
|
+
return detected
|
|
376
|
+
_theme_debug(f'active scheme="{scheme_name}" -> none')
|
|
377
|
+
if has_windows_terminal:
|
|
378
|
+
_theme_debug("windows-terminal default -> dark")
|
|
379
|
+
return "dark"
|
|
380
|
+
return None
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def detect_terminal_theme_if_available(stream: Any | None = None) -> TerminalTheme | None:
|
|
384
|
+
"""Detect the terminal background without guessing when detection fails."""
|
|
385
|
+
for env_name in ("ALYSIS_THEME", "OWL_THEME"):
|
|
386
|
+
raw_value = env_get(env_name)
|
|
387
|
+
requested = _normalize_theme(raw_value)
|
|
388
|
+
_theme_debug(f"env={env_name} -> {requested or 'none'}")
|
|
389
|
+
if requested:
|
|
390
|
+
return requested
|
|
391
|
+
|
|
392
|
+
colorfgbg = os.environ.get("COLORFGBG")
|
|
393
|
+
detected = _theme_from_colorfgbg(colorfgbg)
|
|
394
|
+
_theme_debug(f"COLORFGBG={colorfgbg or 'missing'} -> {detected or 'none'}")
|
|
395
|
+
if detected:
|
|
396
|
+
return detected
|
|
397
|
+
|
|
398
|
+
if os.environ.get("NO_COLOR"):
|
|
399
|
+
_theme_debug("OSC11=skipped (NO_COLOR)")
|
|
400
|
+
elif not _truthy_env("ALYSIS_ENABLE_OSC11"):
|
|
401
|
+
_theme_debug("OSC11=skipped (disabled)")
|
|
402
|
+
else:
|
|
403
|
+
detected = _theme_from_osc11(stream)
|
|
404
|
+
if detected:
|
|
405
|
+
return detected
|
|
406
|
+
|
|
407
|
+
detected = _theme_from_windows_terminal_settings()
|
|
408
|
+
if detected:
|
|
409
|
+
return detected
|
|
410
|
+
|
|
411
|
+
# macOS's global light/dark appearance does not describe a Terminal.app
|
|
412
|
+
# window's active profile. A light desktop can host a dark terminal (and
|
|
413
|
+
# vice versa), so using AppleInterfaceStyle here can select the exact
|
|
414
|
+
# opposite foreground palette. Unknown is safer: callers then inherit the
|
|
415
|
+
# terminal's own foreground and background colours.
|
|
416
|
+
if os.environ.get("TERM_PROGRAM") == "Apple_Terminal":
|
|
417
|
+
_theme_debug("Apple_Terminal profile background unavailable -> none")
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def detect_terminal_theme(stream: Any | None = None) -> TerminalTheme:
|
|
422
|
+
"""Detect the host terminal theme, returning neutral when it is unknown."""
|
|
423
|
+
detected = detect_terminal_theme_if_available(stream)
|
|
424
|
+
if detected:
|
|
425
|
+
_theme_debug(f"result={detected}")
|
|
426
|
+
return detected
|
|
427
|
+
|
|
428
|
+
fallback = _fallback_theme()
|
|
429
|
+
_theme_debug(f"result={fallback}")
|
|
430
|
+
return fallback
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _fallback_theme() -> TerminalTheme:
|
|
434
|
+
for env_name in ("ALYSIS_FALLBACK_THEME", "OWL_FALLBACK_THEME"):
|
|
435
|
+
fallback = _normalize_theme(env_get(env_name))
|
|
436
|
+
if fallback:
|
|
437
|
+
return fallback
|
|
438
|
+
return "neutral"
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
__all__ = [
|
|
442
|
+
"detect_terminal_theme",
|
|
443
|
+
"detect_terminal_theme_if_available",
|
|
444
|
+
"_hex_to_8bit",
|
|
445
|
+
"_load_jsonc_file",
|
|
446
|
+
"_strip_json_comments",
|
|
447
|
+
"_is_wsl",
|
|
448
|
+
"_theme_from_colorfgbg",
|
|
449
|
+
"_theme_from_hex_background",
|
|
450
|
+
"_theme_from_osc11",
|
|
451
|
+
"_theme_from_windows_terminal_settings",
|
|
452
|
+
"_windows_terminal_builtin_scheme_theme",
|
|
453
|
+
"_windows_terminal_scheme_name",
|
|
454
|
+
"_windows_terminal_settings_paths",
|
|
455
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, slots=True)
|
|
8
|
+
class StatusEvent:
|
|
9
|
+
mode: str
|
|
10
|
+
model: str
|
|
11
|
+
workspace: str
|
|
12
|
+
session_id: str
|
|
13
|
+
branch: str
|
|
14
|
+
dirty: bool
|
|
15
|
+
stream: bool
|
|
16
|
+
task: str = "-"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True, slots=True)
|
|
20
|
+
class ToolStartEvent:
|
|
21
|
+
tool_call_id: str
|
|
22
|
+
name: str
|
|
23
|
+
args: dict[str, Any]
|
|
24
|
+
step: int
|
|
25
|
+
subagent_name: str | None = None
|
|
26
|
+
subagent_mode: str | None = None
|
|
27
|
+
nesting_depth: int = 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class ToolOutputEvent:
|
|
32
|
+
tool_call_id: str
|
|
33
|
+
name: str
|
|
34
|
+
chunk: str
|
|
35
|
+
subagent_name: str | None = None
|
|
36
|
+
subagent_mode: str | None = None
|
|
37
|
+
nesting_depth: int = 0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True, slots=True)
|
|
41
|
+
class ToolEndEvent:
|
|
42
|
+
tool_call_id: str
|
|
43
|
+
name: str
|
|
44
|
+
status: str
|
|
45
|
+
elapsed_ms: int
|
|
46
|
+
meta: dict[str, Any] = field(default_factory=dict)
|
|
47
|
+
subagent_name: str | None = None
|
|
48
|
+
subagent_mode: str | None = None
|
|
49
|
+
nesting_depth: int = 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True, slots=True)
|
|
53
|
+
class SubagentStartEvent:
|
|
54
|
+
name: str
|
|
55
|
+
mode: str
|
|
56
|
+
subagent_session_id: str | None = None
|
|
57
|
+
# What the subagent is for (the definition's description). Surfaces may show a
|
|
58
|
+
# condensed form so the user knows what agent they just entered.
|
|
59
|
+
description: str = ""
|
|
60
|
+
subagent_run_id: str | None = None
|
|
61
|
+
workspace_view: str = "shared"
|
|
62
|
+
label: str = ""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True, slots=True)
|
|
66
|
+
class SubagentEndEvent:
|
|
67
|
+
name: str
|
|
68
|
+
mode: str
|
|
69
|
+
status: str
|
|
70
|
+
elapsed_ms: int
|
|
71
|
+
steps_completed: int
|
|
72
|
+
subagent_session_id: str | None = None
|
|
73
|
+
error: str | None = None
|
|
74
|
+
subagent_run_id: str | None = None
|
|
75
|
+
workspace_view: str = "shared"
|
|
76
|
+
label: str = ""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(frozen=True, slots=True)
|
|
80
|
+
class PatchEvent:
|
|
81
|
+
files: list[str]
|
|
82
|
+
diff: str
|
|
83
|
+
summary: str = ""
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True, slots=True)
|
|
87
|
+
class ApprovalRequest:
|
|
88
|
+
kind: str
|
|
89
|
+
reason: str
|
|
90
|
+
preview: str
|
|
91
|
+
files: list[str] = field(default_factory=list)
|
|
92
|
+
command: str | None = None
|
|
93
|
+
metadata: dict[str, object] = field(default_factory=dict)
|
|
94
|
+
allow_for_session_scope: dict[str, object] | None = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(frozen=True, slots=True)
|
|
98
|
+
class ApprovalDecision:
|
|
99
|
+
allow: bool
|
|
100
|
+
allow_for_session: bool = False
|