teddy-cli 0.1.17.dev1399__tar.gz → 0.1.17.dev1401__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.
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/PKG-INFO +1 -1
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/pyproject.toml +1 -1
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/session_cli_handlers.py +176 -5
- teddy_cli-0.1.17.dev1401/src/teddy_executor/adapters/outbound/local_repo_tree_generator.py +138 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/yaml_config_adapter.py +33 -1
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/action_factory.py +2 -2
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/helpers.py +2 -4
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/registries/infrastructure.py +6 -2
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/config.yaml +13 -3
- teddy_cli-0.1.17.dev1399/src/teddy_executor/adapters/outbound/local_repo_tree_generator.py +0 -81
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/.gitignore +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/LICENSE +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/README.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/__main__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/cli_formatter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/cli_helpers.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_app.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_editor.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_execution.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_helpers.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_logic.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_previews.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/inbound/textual_plan_reviewer_widgets.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/console_interactor.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/console_interactor_ask_loop.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/console_interactor_helpers.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/console_tooling.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/filesystem_helpers.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/litellm_adapter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/local_file_system_adapter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/openrouter_hydrator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/shell_adapter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/shell_command_builder.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/suppressed_logging.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/system_environment_adapter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/system_environment_inspector.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/system_time_adapter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/terminal_quit_key_listener.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/web_scraper_adapter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/outbound/web_searcher_adapter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/container.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/action_ports.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/change_set.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/drift_report.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/exceptions.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/execution_report.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/orchestrator_ports.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/plan.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/planning_ports.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/project_context.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/report_assembly_data.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/session.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/shell_output.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/domain/models/web_search_results.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/edit_simulator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/get_context_use_case.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/init.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/plan_parser.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/plan_reviewer.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/plan_validator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/planning_use_case.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/inbound/run_plan_use_case.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/config_service.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/environment_inspector.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/execution_report_assembler.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/file_system_manager.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/llm_client.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/markdown_report_formatter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/prompt_manager.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/quit_key_listener.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/repo_tree_generator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/session_loop_guard.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/session_manager.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/session_repository.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/shell_executor.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/system_environment.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/time_service.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/user_interactor.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/web_scraper.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/ports/outbound/web_searcher.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/action_changeset_builder.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/action_diff_manager.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/action_dispatcher.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/action_executor.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/action_parser_complex.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/action_parser_strategies.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/context_service.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/edit_simulator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/execution_orchestrator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/execution_report_assembler.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/init_service.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/markdown_plan_parser.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/markdown_report_formatter.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/parser_infrastructure.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/parser_metadata.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/parser_reporting.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/plan_validator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/planning_service.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/prompt_manager.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_lifecycle_manager.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_loop_guard.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_orchestrator.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_planner.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_pruning_service.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_replanner.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_repository.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/session_service.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/templates/execution_report.md.j2 +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/update_checker.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/edit.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/edit_matcher.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/edit_matcher_heuristics.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/execute.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/filesystem.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/services/validation_rules/message.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/diff.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/editor_names.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/interrupt_guard.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/interruptible_call.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/io.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/markdown.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/paths.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/serialization.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/session_notices.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/stdin_ownership.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/string.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/core/utils/terminal.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/prompts.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/registries/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/registries/reviewer.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/registries/validators.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/MRP.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/__init__.py +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/init.context +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/prompts/architect.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/prompts/assistant.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/prompts/debugger.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/prompts/developer.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/prompts/pathfinder.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/config/prompts/prototyper.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/ARCHITECTURE.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/PROJECT.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/case-file.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/ci.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/component-design.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/makefile.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/milestone.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/pre-commit.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/specification-document.md +0 -0
- {teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/resources/templates/vertical-slice.md +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
import shutil
|
|
3
4
|
import signal
|
|
4
5
|
import subprocess # nosec B404
|
|
@@ -244,10 +245,179 @@ def _check_git_initialized() -> None:
|
|
|
244
245
|
)
|
|
245
246
|
|
|
246
247
|
|
|
247
|
-
def
|
|
248
|
-
"""
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
def _warn_disabled_workspace_steps(config_service: IConfigService) -> None:
|
|
249
|
+
"""Print one warning per disabled workspace step with a real consequence.
|
|
250
|
+
|
|
251
|
+
A disabled hook installer warns because the unskippable post-commit test
|
|
252
|
+
gate then depends on a hook that never landed. A disabled git init warns
|
|
253
|
+
only when the workspace is not already a repository, because an existing
|
|
254
|
+
repository needs no initialization.
|
|
255
|
+
"""
|
|
256
|
+
if not config_service.get_setting("workspace.pre-commit", True):
|
|
257
|
+
typer.secho(
|
|
258
|
+
"⚠ Pre-commit hook installation is disabled. "
|
|
259
|
+
"The post-commit test gate will not run.",
|
|
260
|
+
fg=typer.colors.YELLOW,
|
|
261
|
+
err=True,
|
|
262
|
+
)
|
|
263
|
+
init_disabled = not config_service.get_setting("workspace.init", True)
|
|
264
|
+
if init_disabled and not (Path.cwd() / ".git").exists():
|
|
265
|
+
typer.secho(
|
|
266
|
+
"⚠ Git repository not initialized. Version control actions will fail.",
|
|
267
|
+
fg=typer.colors.YELLOW,
|
|
268
|
+
err=True,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _run_health_checks(config_service: IConfigService) -> None:
|
|
273
|
+
"""Run each startup health check the workspace toggles allow.
|
|
274
|
+
|
|
275
|
+
A disabled step is skipped, and ``_warn_disabled_workspace_steps`` reports
|
|
276
|
+
the ones that carry a real consequence.
|
|
277
|
+
"""
|
|
278
|
+
if config_service.get_setting("workspace.pre-commit", True):
|
|
279
|
+
_ensure_commit_hooks()
|
|
280
|
+
if config_service.get_setting("workspace.init", True):
|
|
281
|
+
_check_git_initialized()
|
|
282
|
+
_warn_disabled_workspace_steps(config_service)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
_GIT_PULL_TIMEOUT_SECONDS = 15
|
|
286
|
+
_PULL_SKIPPED_WARNING = "⚠ Workspace pull skipped; continuing with the current branch."
|
|
287
|
+
_PULL_TIMEOUT_WARNING = "⚠ Workspace pull timed out; continuing without it."
|
|
288
|
+
_IN_PROGRESS_GIT_MARKERS = (
|
|
289
|
+
"rebase-merge",
|
|
290
|
+
"rebase-apply",
|
|
291
|
+
"MERGE_HEAD",
|
|
292
|
+
"CHERRY_PICK_HEAD",
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _pull_workspace_on_start(
|
|
297
|
+
config_service: IConfigService,
|
|
298
|
+
run_func: Optional[Callable[..., Any]] = None,
|
|
299
|
+
) -> None:
|
|
300
|
+
"""Fast-forward the workspace before starting a new session.
|
|
301
|
+
|
|
302
|
+
The pull runs only when ``workspace.pull`` is enabled and the workspace is
|
|
303
|
+
a Git repository, so a disabled toggle or a non-repository directory stays
|
|
304
|
+
silent. A detached HEAD, a missing upstream, or an in-progress operation
|
|
305
|
+
also stays silent, because there is nothing safe to fast-forward. A dirty
|
|
306
|
+
worktree or a failed pull reports one warning and leaves the workspace
|
|
307
|
+
unchanged. A successful fast-forward names the number of commits it moved.
|
|
308
|
+
|
|
309
|
+
The Git process is injected through ``run_func`` so every decision is
|
|
310
|
+
testable without spawning a real repository.
|
|
311
|
+
"""
|
|
312
|
+
if not config_service.get_setting("workspace.pull", True):
|
|
313
|
+
return
|
|
314
|
+
if not (Path.cwd() / ".git").exists():
|
|
315
|
+
return
|
|
316
|
+
git_run = run_func or subprocess.run
|
|
317
|
+
try:
|
|
318
|
+
_fast_forward_workspace(git_run)
|
|
319
|
+
except subprocess.TimeoutExpired:
|
|
320
|
+
typer.secho(_PULL_TIMEOUT_WARNING, fg=typer.colors.YELLOW, err=True)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _fast_forward_workspace(git_run: Callable[..., Any]) -> None:
|
|
324
|
+
"""Run the pull decision table through the injected Git runner.
|
|
325
|
+
|
|
326
|
+
Each precondition is probed before the pull so an unsafe state never
|
|
327
|
+
reaches ``git pull``. A dirty worktree reports one warning, while every
|
|
328
|
+
other unsafe state stays silent because it has nothing to report.
|
|
329
|
+
"""
|
|
330
|
+
skip_reason = _pull_skip_reason(git_run)
|
|
331
|
+
if skip_reason is not None:
|
|
332
|
+
if skip_reason:
|
|
333
|
+
typer.secho(skip_reason, fg=typer.colors.YELLOW, err=True)
|
|
334
|
+
return
|
|
335
|
+
before = _read_head(git_run)
|
|
336
|
+
result = git_run(
|
|
337
|
+
["git", "pull", "--ff-only"],
|
|
338
|
+
env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
|
|
339
|
+
capture_output=True,
|
|
340
|
+
text=True,
|
|
341
|
+
timeout=_GIT_PULL_TIMEOUT_SECONDS,
|
|
342
|
+
)
|
|
343
|
+
if result.returncode != 0:
|
|
344
|
+
typer.secho(_PULL_SKIPPED_WARNING, fg=typer.colors.YELLOW, err=True)
|
|
345
|
+
return
|
|
346
|
+
_report_fast_forward(git_run, before, _read_head(git_run))
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _pull_skip_reason(git_run: Callable[..., Any]) -> Optional[str]:
|
|
350
|
+
"""Return the outcome text to print, or None when the pull may proceed.
|
|
351
|
+
|
|
352
|
+
An empty string marks a silent skip, because the workspace holds no safe
|
|
353
|
+
fast-forward and reporting it would only add noise. A dirty worktree
|
|
354
|
+
returns the skipped warning, because tracked changes can conflict.
|
|
355
|
+
"""
|
|
356
|
+
git_dir = Path.cwd() / ".git"
|
|
357
|
+
if any((git_dir / marker).exists() for marker in _IN_PROGRESS_GIT_MARKERS):
|
|
358
|
+
return ""
|
|
359
|
+
detached = git_run(
|
|
360
|
+
["git", "symbolic-ref", "-q", "HEAD"],
|
|
361
|
+
capture_output=True,
|
|
362
|
+
timeout=_GIT_PULL_TIMEOUT_SECONDS,
|
|
363
|
+
)
|
|
364
|
+
if detached.returncode != 0:
|
|
365
|
+
return ""
|
|
366
|
+
upstream = git_run(
|
|
367
|
+
["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
|
|
368
|
+
capture_output=True,
|
|
369
|
+
timeout=_GIT_PULL_TIMEOUT_SECONDS,
|
|
370
|
+
)
|
|
371
|
+
if upstream.returncode != 0:
|
|
372
|
+
return ""
|
|
373
|
+
status = git_run(
|
|
374
|
+
["git", "status", "--porcelain"],
|
|
375
|
+
capture_output=True,
|
|
376
|
+
text=True,
|
|
377
|
+
timeout=_GIT_PULL_TIMEOUT_SECONDS,
|
|
378
|
+
)
|
|
379
|
+
tracked_changes = [
|
|
380
|
+
line
|
|
381
|
+
for line in status.stdout.splitlines()
|
|
382
|
+
if line and not line.startswith("??")
|
|
383
|
+
]
|
|
384
|
+
if tracked_changes:
|
|
385
|
+
return _PULL_SKIPPED_WARNING
|
|
386
|
+
return None
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _read_head(git_run: Callable[..., Any]) -> str:
|
|
390
|
+
"""Read the current commit id through the injected Git runner."""
|
|
391
|
+
return git_run(
|
|
392
|
+
["git", "rev-parse", "HEAD"],
|
|
393
|
+
capture_output=True,
|
|
394
|
+
text=True,
|
|
395
|
+
timeout=_GIT_PULL_TIMEOUT_SECONDS,
|
|
396
|
+
).stdout.strip()
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _report_fast_forward(git_run: Callable[..., Any], before: str, after: str) -> None:
|
|
400
|
+
"""Report how many commits the fast-forward moved, or stay silent.
|
|
401
|
+
|
|
402
|
+
A pull that moves no commit has nothing to report, so it prints nothing.
|
|
403
|
+
"""
|
|
404
|
+
if after == before:
|
|
405
|
+
return
|
|
406
|
+
count_output = git_run(
|
|
407
|
+
["git", "rev-list", "--count", f"{before}..{after}"],
|
|
408
|
+
capture_output=True,
|
|
409
|
+
text=True,
|
|
410
|
+
timeout=_GIT_PULL_TIMEOUT_SECONDS,
|
|
411
|
+
).stdout.strip()
|
|
412
|
+
commits = int(count_output) if count_output else 0
|
|
413
|
+
if commits <= 0:
|
|
414
|
+
return
|
|
415
|
+
noun = "commit" if commits == 1 else "commits"
|
|
416
|
+
typer.secho(
|
|
417
|
+
f"Workspace fast-forwarded ({commits} {noun})",
|
|
418
|
+
fg=typer.colors.GREEN,
|
|
419
|
+
err=True,
|
|
420
|
+
)
|
|
251
421
|
|
|
252
422
|
|
|
253
423
|
def _start_background_update_check(container: Container) -> list[str]:
|
|
@@ -285,7 +455,7 @@ def _run_startup_preamble(
|
|
|
285
455
|
its ``agent`` so the preflight validates the agent prompt; ``resume``
|
|
286
456
|
leaves it ``None`` and validates no agent.
|
|
287
457
|
"""
|
|
288
|
-
_run_health_checks()
|
|
458
|
+
_run_health_checks(container.resolve(IConfigService))
|
|
289
459
|
typer.echo("Checking configurations...", err=True)
|
|
290
460
|
fired.extend(
|
|
291
461
|
_run_cli_preflight_check(
|
|
@@ -692,6 +862,7 @@ def handle_new_session( # noqa: PLR0913
|
|
|
692
862
|
setup_editor=setup_editor,
|
|
693
863
|
setup_api_key=setup_api_key,
|
|
694
864
|
)
|
|
865
|
+
_pull_workspace_on_start(container.resolve(IConfigService))
|
|
695
866
|
_echo_config_success(container, agent, model=model)
|
|
696
867
|
|
|
697
868
|
session_manager: ISessionManager = container.resolve(ISessionManager)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from teddy_executor.core.ports.outbound.repo_tree_generator import IRepoTreeGenerator
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
UNREADABLE_MARKER = "[... directory not expanded (unreadable): 0 folders, 0 files]"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LocalRepoTreeGenerator(IRepoTreeGenerator):
|
|
12
|
+
"""
|
|
13
|
+
An adapter that generates a file tree for the local repository,
|
|
14
|
+
respecting .gitignore and .teddyignore rules.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, root_dir: str = ".", max_depth: int = 0):
|
|
18
|
+
from teddy_executor.adapters.outbound.filesystem_helpers import (
|
|
19
|
+
load_ignore_spec,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
assert max_depth >= 0, f"max_depth must be non-negative, got {max_depth}"
|
|
23
|
+
self.root_dir = Path(root_dir).resolve()
|
|
24
|
+
self.ignore_spec = load_ignore_spec(self.root_dir)
|
|
25
|
+
self._max_depth = max_depth
|
|
26
|
+
|
|
27
|
+
def generate_tree(self) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Generates a string representation of the file tree in a single pass.
|
|
30
|
+
"""
|
|
31
|
+
sections: list[tuple[str, str, list[str]]] = []
|
|
32
|
+
root_names = self._collect_directory(self.root_dir, "", sections)
|
|
33
|
+
# Sections sort by their relative path, and the root resolves to ".",
|
|
34
|
+
# so a top-level name below "." (for example "!important") renders
|
|
35
|
+
# before the root block. The "." key preserves that exact order.
|
|
36
|
+
sections.append((".", "", root_names))
|
|
37
|
+
sections.sort(key=lambda item: item[0])
|
|
38
|
+
|
|
39
|
+
rendered: list[str] = []
|
|
40
|
+
for _sort_key, relative_path, names in sections:
|
|
41
|
+
if not names:
|
|
42
|
+
continue
|
|
43
|
+
if relative_path:
|
|
44
|
+
rendered.append("\n".join([f"./{relative_path}:"] + names))
|
|
45
|
+
else:
|
|
46
|
+
rendered.append("\n".join(names))
|
|
47
|
+
return "\n\n".join(rendered)
|
|
48
|
+
|
|
49
|
+
def _collect_directory(
|
|
50
|
+
self,
|
|
51
|
+
directory: str | Path,
|
|
52
|
+
relative_path: str,
|
|
53
|
+
sections: list[tuple[str, str, list[str]]],
|
|
54
|
+
depth: int = 0,
|
|
55
|
+
) -> list[str]:
|
|
56
|
+
"""
|
|
57
|
+
Collects one directory's visible names and recurses into its children.
|
|
58
|
+
|
|
59
|
+
A directory at or beyond the configured depth limit renders one
|
|
60
|
+
omission marker instead of expanding, so truncation is never silent.
|
|
61
|
+
An unreadable directory renders an explicit marker rather than an
|
|
62
|
+
empty section, so it never looks like a readable empty directory.
|
|
63
|
+
Directory and symlink flags come from the scan cache, so each entry
|
|
64
|
+
costs no additional stat call. Symlinks are listed as files and are
|
|
65
|
+
never followed, which keeps circular links from recursing.
|
|
66
|
+
"""
|
|
67
|
+
if self._max_depth > 0 and depth >= self._max_depth:
|
|
68
|
+
marker = self._depth_limit_marker(directory, relative_path)
|
|
69
|
+
return [marker] if marker else []
|
|
70
|
+
|
|
71
|
+
entries = self._scan_visible_entries(directory, relative_path)
|
|
72
|
+
if entries is None:
|
|
73
|
+
return [UNREADABLE_MARKER]
|
|
74
|
+
|
|
75
|
+
names: list[str] = []
|
|
76
|
+
for entry, is_directory, child_path in entries:
|
|
77
|
+
names.append(entry.name)
|
|
78
|
+
if is_directory:
|
|
79
|
+
child_names = self._collect_directory(
|
|
80
|
+
entry.path, child_path, sections, depth + 1
|
|
81
|
+
)
|
|
82
|
+
sort_key = child_path.replace("/", os.sep).lower()
|
|
83
|
+
sections.append((sort_key, child_path, child_names))
|
|
84
|
+
|
|
85
|
+
names.sort(key=str.lower)
|
|
86
|
+
return names
|
|
87
|
+
|
|
88
|
+
def _scan_visible_entries(
|
|
89
|
+
self, directory: str | Path, relative_path: str
|
|
90
|
+
) -> list[tuple[os.DirEntry, bool, str]] | None:
|
|
91
|
+
"""
|
|
92
|
+
Returns one visible immediate child per entry after ignore rules.
|
|
93
|
+
|
|
94
|
+
The scan is materialized before its context closes, so each DirEntry
|
|
95
|
+
still answers its cached directory flag. An unreadable directory
|
|
96
|
+
returns ``None`` so callers can tell it apart from a readable empty
|
|
97
|
+
one, which returns an empty list.
|
|
98
|
+
"""
|
|
99
|
+
try:
|
|
100
|
+
with os.scandir(directory) as iterator:
|
|
101
|
+
entries = list(iterator)
|
|
102
|
+
except PermissionError:
|
|
103
|
+
logger.debug("Skipped unreadable directory %s", directory, exc_info=True)
|
|
104
|
+
return None
|
|
105
|
+
visible: list[tuple[os.DirEntry, bool, str]] = []
|
|
106
|
+
for entry in entries:
|
|
107
|
+
is_directory = entry.is_dir(follow_symlinks=False)
|
|
108
|
+
child_path = (
|
|
109
|
+
f"{relative_path}/{entry.name}" if relative_path else entry.name
|
|
110
|
+
)
|
|
111
|
+
match_path = (child_path + "/") if is_directory else child_path
|
|
112
|
+
if self.ignore_spec.match_file(match_path):
|
|
113
|
+
continue
|
|
114
|
+
visible.append((entry, is_directory, child_path))
|
|
115
|
+
return visible
|
|
116
|
+
|
|
117
|
+
def _depth_limit_marker(self, directory: str | Path, relative_path: str) -> str:
|
|
118
|
+
"""
|
|
119
|
+
Renders the omission marker for a directory at the depth limit.
|
|
120
|
+
|
|
121
|
+
Counts only the visible immediate children after ignore rules. An
|
|
122
|
+
unreadable directory renders the unreadable marker instead. Returns an
|
|
123
|
+
empty string when no child is visible, so a genuinely empty directory
|
|
124
|
+
renders no section rather than a misleading marker.
|
|
125
|
+
"""
|
|
126
|
+
entries = self._scan_visible_entries(directory, relative_path)
|
|
127
|
+
if entries is None:
|
|
128
|
+
return UNREADABLE_MARKER
|
|
129
|
+
folders = sum(1 for _entry, is_directory, _path in entries if is_directory)
|
|
130
|
+
files = len(entries) - folders
|
|
131
|
+
if not folders and not files:
|
|
132
|
+
return ""
|
|
133
|
+
folder_noun = "folder" if folders == 1 else "folders"
|
|
134
|
+
file_noun = "file" if files == 1 else "files"
|
|
135
|
+
return (
|
|
136
|
+
f"[... directory not expanded (depth limit {self._max_depth}): "
|
|
137
|
+
f"{folders} {folder_noun}, {files} {file_noun}]"
|
|
138
|
+
)
|
|
@@ -14,6 +14,13 @@ _INTERPOLATION_PATTERN = re.compile(
|
|
|
14
14
|
|
|
15
15
|
_MERGE_TAG = "tag:yaml.org,2002:merge"
|
|
16
16
|
|
|
17
|
+
# Legacy ``execution.<key>`` names and the canonical group each one now lives in.
|
|
18
|
+
_LEGACY_EXECUTION_KEY_GROUPS = {
|
|
19
|
+
"default_timeout_seconds": "execute",
|
|
20
|
+
"max_output_lines": "execute",
|
|
21
|
+
"similarity_threshold": "edit",
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
def _strict_load(text: str, source_path: str) -> Any:
|
|
19
26
|
"""Parse ``text`` and reject a repeated mapping key at any depth."""
|
|
@@ -114,10 +121,35 @@ class YamlConfigAdapter(IConfigService):
|
|
|
114
121
|
try:
|
|
115
122
|
with open(self._config_path, "r", encoding="utf-8") as f:
|
|
116
123
|
data = _strict_load(f.read(), self._config_path)
|
|
117
|
-
|
|
124
|
+
user_config = data if isinstance(data, dict) else {}
|
|
118
125
|
except (yaml.YAMLError, OSError):
|
|
119
126
|
return {}
|
|
120
127
|
|
|
128
|
+
self._migrate_legacy_execution_keys(user_config)
|
|
129
|
+
return user_config
|
|
130
|
+
|
|
131
|
+
def _migrate_legacy_execution_keys(self, user_config: Dict[str, Any]) -> None:
|
|
132
|
+
"""Copies tuned legacy ``execution.*`` values to their canonical groups.
|
|
133
|
+
|
|
134
|
+
A user file written before the execute/edit split still holds these
|
|
135
|
+
keys. The copy runs on the in-memory user dict before the baseline
|
|
136
|
+
merge, so the canonical key resolves while the user file stays
|
|
137
|
+
untouched. An absent or non-mapping legacy parent is left alone, a
|
|
138
|
+
``None`` value is skipped, and an existing canonical key wins.
|
|
139
|
+
"""
|
|
140
|
+
legacy = user_config.get("execution")
|
|
141
|
+
if not isinstance(legacy, dict):
|
|
142
|
+
return
|
|
143
|
+
for legacy_key, group in _LEGACY_EXECUTION_KEY_GROUPS.items():
|
|
144
|
+
value = legacy.get(legacy_key)
|
|
145
|
+
if value is None:
|
|
146
|
+
continue
|
|
147
|
+
target = user_config.get(group)
|
|
148
|
+
if not isinstance(target, dict):
|
|
149
|
+
target = {}
|
|
150
|
+
user_config[group] = target
|
|
151
|
+
target.setdefault(legacy_key, value)
|
|
152
|
+
|
|
121
153
|
def _merge_dicts(self, base: Dict[str, Any], overrides: Dict[str, Any]) -> None:
|
|
122
154
|
"""Recursively merges overrides into base. Prunes keys set to None."""
|
|
123
155
|
for key, value in overrides.items():
|
|
@@ -135,7 +135,7 @@ class ActionFactory(IActionFactory):
|
|
|
135
135
|
if "timeout" not in execute_params and self._config_service:
|
|
136
136
|
# Safe-by-Default: Provide hardcoded 60.0 fallback if config is missing
|
|
137
137
|
default_timeout = self._config_service.get_setting(
|
|
138
|
-
"
|
|
138
|
+
"execute.default_timeout_seconds", 60.0
|
|
139
139
|
)
|
|
140
140
|
if default_timeout is not None:
|
|
141
141
|
from teddy_executor.core.services.parser_infrastructure import (
|
|
@@ -154,7 +154,7 @@ class ActionFactory(IActionFactory):
|
|
|
154
154
|
if "similarity_threshold" not in kwargs and self._config_service:
|
|
155
155
|
# Safe-by-Default: Provide domain default if config is missing
|
|
156
156
|
global_threshold = self._config_service.get_setting(
|
|
157
|
-
"
|
|
157
|
+
"edit.similarity_threshold", DEFAULT_SIMILARITY_THRESHOLD
|
|
158
158
|
)
|
|
159
159
|
if global_threshold is not None:
|
|
160
160
|
from teddy_executor.core.services.parser_infrastructure import (
|
|
@@ -131,7 +131,7 @@ def resolve_similarity_threshold(
|
|
|
131
131
|
Resolves in order: Action Params -> Global Config -> Domain Default.
|
|
132
132
|
"""
|
|
133
133
|
global_threshold_raw = config_service.get_setting(
|
|
134
|
-
"
|
|
134
|
+
"edit.similarity_threshold", DEFAULT_SIMILARITY_THRESHOLD
|
|
135
135
|
)
|
|
136
136
|
global_threshold = (
|
|
137
137
|
float(global_threshold_raw)
|
|
@@ -139,9 +139,7 @@ def resolve_similarity_threshold(
|
|
|
139
139
|
else DEFAULT_SIMILARITY_THRESHOLD
|
|
140
140
|
)
|
|
141
141
|
|
|
142
|
-
threshold_raw = action_params.get(
|
|
143
|
-
"execution.similarity_threshold", global_threshold
|
|
144
|
-
)
|
|
142
|
+
threshold_raw = action_params.get("edit.similarity_threshold", global_threshold)
|
|
145
143
|
return float(threshold_raw) if threshold_raw is not None else global_threshold
|
|
146
144
|
|
|
147
145
|
|
|
@@ -76,7 +76,7 @@ def register_infrastructure(container: punq.Container) -> None:
|
|
|
76
76
|
factory=lambda: ShellAdapter(
|
|
77
77
|
command_builder=container.resolve(ShellCommandBuilder),
|
|
78
78
|
max_execute_lines=container.resolve(IConfigService).get_setting(
|
|
79
|
-
"
|
|
79
|
+
"execute.max_output_lines", 100
|
|
80
80
|
),
|
|
81
81
|
# Shared process-global guard: the SAME instance the session-loop
|
|
82
82
|
# boundary installs and the orchestrator drains, so an in-flight
|
|
@@ -143,7 +143,11 @@ def register_infrastructure(container: punq.Container) -> None:
|
|
|
143
143
|
)
|
|
144
144
|
container.register(
|
|
145
145
|
IRepoTreeGenerator,
|
|
146
|
-
factory=lambda: LocalRepoTreeGenerator(
|
|
146
|
+
factory=lambda: LocalRepoTreeGenerator(
|
|
147
|
+
max_depth=container.resolve(IConfigService).get_setting(
|
|
148
|
+
"context.max_tree_depth", 0
|
|
149
|
+
)
|
|
150
|
+
),
|
|
147
151
|
scope=punq.Scope.transient,
|
|
148
152
|
)
|
|
149
153
|
container.register(
|
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
editor: ""
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
default_timeout_seconds: 60 # Default timeout for EXECUTE actions
|
|
7
|
-
similarity_threshold: 0.95 # EDIT match threshold (1.0 = exact match; ignores relative indentation).
|
|
5
|
+
execute:
|
|
6
|
+
default_timeout_seconds: 60 # Default timeout for EXECUTE actions.
|
|
8
7
|
max_output_lines: 100 # Caps EXECUTE stdout/stderr output lines.
|
|
9
8
|
|
|
9
|
+
edit:
|
|
10
|
+
similarity_threshold: 0.95 # EDIT match threshold (1.0 = exact match; ignores relative indentation).
|
|
11
|
+
|
|
12
|
+
workspace:
|
|
13
|
+
init: true # Initialize a missing Git repository (start and resume).
|
|
14
|
+
pre-commit: true # Install pre-commit and post-commit git hooks (start and resume).
|
|
15
|
+
pull: true # Fast-forward pull before starting a new session (start only).
|
|
16
|
+
|
|
10
17
|
read:
|
|
11
18
|
max_lines: 1000 # Caps READ action output lines.
|
|
12
19
|
|
|
20
|
+
context:
|
|
21
|
+
max_tree_depth: 4 # Max project tree depth (0 = unlimited).
|
|
22
|
+
|
|
13
23
|
research:
|
|
14
24
|
max_results: 5 # Number of search results to fetch and scrape.
|
|
15
25
|
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import os
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from teddy_executor.core.ports.outbound.repo_tree_generator import IRepoTreeGenerator
|
|
5
|
-
|
|
6
|
-
logger = logging.getLogger(__name__)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class LocalRepoTreeGenerator(IRepoTreeGenerator):
|
|
10
|
-
"""
|
|
11
|
-
An adapter that generates a file tree for the local repository,
|
|
12
|
-
respecting .gitignore and .teddyignore rules.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
def __init__(self, root_dir: str = "."):
|
|
16
|
-
from teddy_executor.adapters.outbound.filesystem_helpers import (
|
|
17
|
-
load_ignore_spec,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
self.root_dir = Path(root_dir).resolve()
|
|
21
|
-
self.ignore_spec = load_ignore_spec(self.root_dir)
|
|
22
|
-
|
|
23
|
-
def generate_tree(self) -> str:
|
|
24
|
-
"""
|
|
25
|
-
Generates a string representation of the file tree in a single pass.
|
|
26
|
-
"""
|
|
27
|
-
sections: list[tuple[str, str, list[str]]] = []
|
|
28
|
-
root_names = self._collect_directory(self.root_dir, "", sections)
|
|
29
|
-
# Sections sort by their relative path, and the root resolves to ".",
|
|
30
|
-
# so a top-level name below "." (for example "!important") renders
|
|
31
|
-
# before the root block. The "." key preserves that exact order.
|
|
32
|
-
sections.append((".", "", root_names))
|
|
33
|
-
sections.sort(key=lambda item: item[0])
|
|
34
|
-
|
|
35
|
-
rendered: list[str] = []
|
|
36
|
-
for _sort_key, relative_path, names in sections:
|
|
37
|
-
if not names:
|
|
38
|
-
continue
|
|
39
|
-
if relative_path:
|
|
40
|
-
rendered.append("\n".join([f"./{relative_path}:"] + names))
|
|
41
|
-
else:
|
|
42
|
-
rendered.append("\n".join(names))
|
|
43
|
-
return "\n\n".join(rendered)
|
|
44
|
-
|
|
45
|
-
def _collect_directory(
|
|
46
|
-
self,
|
|
47
|
-
directory: str | Path,
|
|
48
|
-
relative_path: str,
|
|
49
|
-
sections: list[tuple[str, str, list[str]]],
|
|
50
|
-
) -> list[str]:
|
|
51
|
-
"""
|
|
52
|
-
Collects one directory's visible names and recurses into its children.
|
|
53
|
-
|
|
54
|
-
Directory and symlink flags come from the scan cache, so each entry
|
|
55
|
-
costs no additional stat call. Symlinks are listed as files and are
|
|
56
|
-
never followed, which keeps circular links from recursing.
|
|
57
|
-
"""
|
|
58
|
-
names: list[str] = []
|
|
59
|
-
try:
|
|
60
|
-
with os.scandir(directory) as iterator:
|
|
61
|
-
entries = list(iterator)
|
|
62
|
-
except PermissionError:
|
|
63
|
-
logger.debug("Skipping unreadable directory %s", directory, exc_info=True)
|
|
64
|
-
return names
|
|
65
|
-
|
|
66
|
-
for entry in entries:
|
|
67
|
-
is_directory = entry.is_dir(follow_symlinks=False)
|
|
68
|
-
child_path = (
|
|
69
|
-
f"{relative_path}/{entry.name}" if relative_path else entry.name
|
|
70
|
-
)
|
|
71
|
-
match_path = (child_path + "/") if is_directory else child_path
|
|
72
|
-
if self.ignore_spec.match_file(match_path):
|
|
73
|
-
continue
|
|
74
|
-
names.append(entry.name)
|
|
75
|
-
if is_directory:
|
|
76
|
-
child_names = self._collect_directory(entry.path, child_path, sections)
|
|
77
|
-
sort_key = child_path.replace("/", os.sep).lower()
|
|
78
|
-
sections.append((sort_key, child_path, child_names))
|
|
79
|
-
|
|
80
|
-
names.sort(key=str.lower)
|
|
81
|
-
return names
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{teddy_cli-0.1.17.dev1399 → teddy_cli-0.1.17.dev1401}/src/teddy_executor/adapters/__init__.py
RENAMED
|
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
|