tunacode-cli 0.0.55__tar.gz → 0.0.56__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.
Potentially problematic release.
This version of tunacode-cli might be problematic. Click here for more details.
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/CLAUDE.md +2 -2
- {tunacode_cli-0.0.55/src/tunacode_cli.egg-info → tunacode_cli-0.0.56}/PKG-INFO +4 -3
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/README.md +2 -2
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/pyproject.toml +58 -1
- tunacode_cli-0.0.56/src/tunacode/cli/commands/implementations/plan.py +50 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/registry.py +3 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl.py +335 -1
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl_components/output_display.py +18 -1
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl_components/tool_executor.py +9 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/constants.py +4 -2
- tunacode_cli-0.0.56/src/tunacode/core/agents/agent_components/agent_config.py +236 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/node_processor.py +44 -42
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/state.py +44 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/tool_handler.py +20 -0
- tunacode_cli-0.0.56/src/tunacode/tools/exit_plan_mode.py +191 -0
- tunacode_cli-0.0.56/src/tunacode/tools/present_plan.py +208 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/types.py +57 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/input.py +13 -2
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/keybindings.py +24 -4
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/panels.py +23 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/prompt_manager.py +19 -2
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/tool_ui.py +3 -2
- tunacode_cli-0.0.56/src/tunacode/utils/message_utils.py +29 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56/src/tunacode_cli.egg-info}/PKG-INFO +4 -3
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode_cli.egg-info/SOURCES.txt +4 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode_cli.egg-info/requires.txt +1 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/agent/test_agent_creation.py +5 -3
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_output_display_logic.py +1 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/test_characterization_main.py +3 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/test_characterization_repl.py +5 -1
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/ui/test_spinner_messages.py +2 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_agent_output_formatting.py +1 -0
- tunacode_cli-0.0.56/tests/test_plan_mode.py +180 -0
- tunacode_cli-0.0.55/src/tunacode/core/agents/agent_components/agent_config.py +0 -109
- tunacode_cli-0.0.55/src/tunacode/utils/message_utils.py +0 -19
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/LICENSE +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/MANIFEST.in +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/setup.cfg +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/setup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/base.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/conversation.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/debug.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/development.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/model.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/system.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/template.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/implementations/todo.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/commands/template_shortcut.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/main.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl_components/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl_components/command_parser.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl_components/error_recovery.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/configuration/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/configuration/defaults.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/configuration/models.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/configuration/settings.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/context.py +0 -0
- {tunacode_cli-0.0.55/src/tunacode/utils → tunacode_cli-0.0.56/src/tunacode/core}/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/agent_helpers.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/json_tool_parser.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/message_handler.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/response_state.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/result_wrapper.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/task_completion.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/tool_buffer.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/tool_executor.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/agent_components/truncation_checker.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/main.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/agents/utils.py +0 -0
- {tunacode_cli-0.0.55/src/tunacode/core/llm → tunacode_cli-0.0.56/src/tunacode/core/background}/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/background/manager.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/code_index.py +0 -0
- {tunacode_cli-0.0.55/src/tunacode/core/background → tunacode_cli-0.0.56/src/tunacode/core/llm}/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/logging/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/logging/config.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/logging/formatters.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/logging/handlers.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/logging/logger.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/agent_setup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/base.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/config_setup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/coordinator.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/environment_setup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/git_safety_setup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/setup/template_setup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/token_usage/api_response_parser.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/token_usage/cost_calculator.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/core/token_usage/usage_tracker.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/exceptions.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/prompts/system.md +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/py.typed +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/services/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/services/mcp.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/setup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/templates/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/templates/loader.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/base.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/bash.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/glob.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/grep.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/grep_components/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/grep_components/file_filter.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/grep_components/pattern_matcher.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/grep_components/result_formatter.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/grep_components/search_result.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/list_dir.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/read_file.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/read_file_async_poc.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/run_command.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/todo.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/update_file.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/tools/write_file.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/completers.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/console.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/constants.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/decorators.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/lexers.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/logging_compat.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/output.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/tool_descriptions.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/utils.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/ui/validators.py +0 -0
- {tunacode_cli-0.0.55/src/tunacode/core → tunacode_cli-0.0.56/src/tunacode/utils}/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/bm25.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/diff_utils.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/file_utils.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/import_cache.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/retry.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/ripgrep.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/security.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/system.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/text_utils.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/token_counter.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/utils/user_configuration.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode_cli.egg-info/top_level.txt +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/agent/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/agent/conftest.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/agent/test_json_tool_parsing.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/agent/test_process_node.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/agent/test_process_request.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/agent/test_tool_message_patching.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/background/test_background_edge_cases.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/background/test_cleanup.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/background/test_task_cancellation.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/background/test_task_creation.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/background/test_task_execution.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/code_index/test_cache_management.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/code_index/test_file_scanning.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/code_index/test_index_building.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/code_index/test_search_operations.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/code_index/test_symbol_extraction.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/commands/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/commands/test_init_command.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/conftest.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/context/__init__.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/context/test_context_acceptance.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/context/test_context_integration.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/context/test_context_loading.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/context/test_tunacode_logging.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_command_parsing.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_error_handling.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_escape_key_behavior.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_input_handling.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_keyboard_interrupts.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_multiline_input.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_repl_initialization.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/repl/test_session_flow.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/services/test_error_recovery.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/services/test_llm_routing.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/services/test_mcp_integration.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/services/test_service_lifecycle.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/state/test_agent_tracking.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/state/test_message_history.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/state/test_permissions.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/state/test_session_management.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/state/test_state_initialization.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/state/test_user_config.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/test_characterization_commands.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/test_characterization_grep.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/ui/test_async_ui.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/ui/test_console_output.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/ui/test_diff_display.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/ui/test_prompt_rendering.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/ui/test_tool_confirmations.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/utils/test_expand_file_refs.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/utils/test_file_operations.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/utils/test_git_commands.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/utils/test_token_counting.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/characterization/utils/test_utils_edge_cases.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/conftest.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_completion_detection.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_json_retry.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_logging_config.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_phase2_type_hints.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_prompt_changes_validation.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_security.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_spinner_updates.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/test_tool_batching_retry.py +0 -0
- {tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/tests/unit/test_constants_enums.py +0 -0
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
- update the documents @documentation and in .claude after any update.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- use the subagent tech-docs-maintainer to update the documentation you MUST instruct the subagent to keep doc updates short you will be PUNISHED for not telling the documentation agent to keep it to only the most distilled information
|
|
5
|
+
- use the subagent tech-docs-maintainer to update the documentation you MUST instruct the subagent to keep doc updates short you will be PUNISHED for not telling the documentation agent to keep it to only the most distilled information
|
|
7
6
|
|
|
8
7
|
- always follow best practices with git commits naming and gh cli workflows
|
|
9
8
|
|
|
@@ -13,6 +12,7 @@
|
|
|
13
12
|
|
|
14
13
|
### Python Coding Standards
|
|
15
14
|
|
|
15
|
+
- always use the venv
|
|
16
16
|
- Use type hints (PEP 484) for all function signatures
|
|
17
17
|
- Prefer f-strings (PEP 498) over %-formatting or .format()
|
|
18
18
|
- Use pathlib.Path instead of os.path for filesystem operations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tunacode-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.56
|
|
4
4
|
Summary: Your agentic CLI developer.
|
|
5
5
|
Author-email: larock22 <noreply@github.com>
|
|
6
6
|
License: MIT
|
|
@@ -39,6 +39,7 @@ Requires-Dist: vulture>=2.7; extra == "dev"
|
|
|
39
39
|
Requires-Dist: unimport>=1.0.0; extra == "dev"
|
|
40
40
|
Requires-Dist: autoflake>=2.0.0; extra == "dev"
|
|
41
41
|
Requires-Dist: dead>=1.5.0; extra == "dev"
|
|
42
|
+
Requires-Dist: hatch>=1.6.0; extra == "dev"
|
|
42
43
|
Dynamic: license-file
|
|
43
44
|
|
|
44
45
|
# TunaCode CLI
|
|
@@ -106,7 +107,7 @@ tunacode --model "anthropic:claude-3.5-sonnet" --key "sk-ant-your-anthropic-key"
|
|
|
106
107
|
tunacode --model "openrouter:openai/gpt-4o" --key "sk-or-your-openrouter-key"
|
|
107
108
|
```
|
|
108
109
|
|
|
109
|
-
Your config is saved to `~/.config/tunacode.json` (edit directly with `nvim ~/.config/tunacode.json`)
|
|
110
|
+
Your config is saved to `~/.config/tunacode.json`. This file stores your API keys, model preferences, and runtime settings like `max_iterations` (default: 40) and `context_window_size`. You can edit it directly with `nvim ~/.config/tunacode.json` or see [the complete configuration example](documentation/configuration/config-file-example.md) for all available options.
|
|
110
111
|
|
|
111
112
|
### Recommended Models
|
|
112
113
|
|
|
@@ -243,7 +244,7 @@ tunacode --model "anthropic:claude-3.5-sonnet" --key "sk-ant-your-anthropic-key"
|
|
|
243
244
|
tunacode --model "openrouter:openai/gpt-4o" --key "sk-or-your-openrouter-key"
|
|
244
245
|
```
|
|
245
246
|
|
|
246
|
-
Your config is saved to `~/.config/tunacode.json` (edit directly with `nvim ~/.config/tunacode.json`)
|
|
247
|
+
Your config is saved to `~/.config/tunacode.json`. This file stores your API keys, model preferences, and runtime settings like `max_iterations` (default: 40) and `context_window_size`. You can edit it directly with `nvim ~/.config/tunacode.json` or see [the complete configuration example](documentation/configuration/config-file-example.md) for all available options.
|
|
247
248
|
|
|
248
249
|
### Recommended Models
|
|
249
250
|
|
|
@@ -63,7 +63,7 @@ tunacode --model "anthropic:claude-3.5-sonnet" --key "sk-ant-your-anthropic-key"
|
|
|
63
63
|
tunacode --model "openrouter:openai/gpt-4o" --key "sk-or-your-openrouter-key"
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Your config is saved to `~/.config/tunacode.json` (edit directly with `nvim ~/.config/tunacode.json`)
|
|
66
|
+
Your config is saved to `~/.config/tunacode.json`. This file stores your API keys, model preferences, and runtime settings like `max_iterations` (default: 40) and `context_window_size`. You can edit it directly with `nvim ~/.config/tunacode.json` or see [the complete configuration example](documentation/configuration/config-file-example.md) for all available options.
|
|
67
67
|
|
|
68
68
|
### Recommended Models
|
|
69
69
|
|
|
@@ -200,7 +200,7 @@ tunacode --model "anthropic:claude-3.5-sonnet" --key "sk-ant-your-anthropic-key"
|
|
|
200
200
|
tunacode --model "openrouter:openai/gpt-4o" --key "sk-or-your-openrouter-key"
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
Your config is saved to `~/.config/tunacode.json` (edit directly with `nvim ~/.config/tunacode.json`)
|
|
203
|
+
Your config is saved to `~/.config/tunacode.json`. This file stores your API keys, model preferences, and runtime settings like `max_iterations` (default: 40) and `context_window_size`. You can edit it directly with `nvim ~/.config/tunacode.json` or see [the complete configuration example](documentation/configuration/config-file-example.md) for all available options.
|
|
204
204
|
|
|
205
205
|
### Recommended Models
|
|
206
206
|
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tunacode-cli"
|
|
7
7
|
|
|
8
|
-
version = "0.0.
|
|
8
|
+
version = "0.0.56"
|
|
9
9
|
description = "Your agentic CLI developer."
|
|
10
10
|
keywords = ["cli", "agent", "development", "automation"]
|
|
11
11
|
readme = "README.md"
|
|
@@ -50,6 +50,7 @@ dev = [
|
|
|
50
50
|
"unimport>=1.0.0",
|
|
51
51
|
"autoflake>=2.0.0",
|
|
52
52
|
"dead>=1.5.0",
|
|
53
|
+
"hatch>=1.6.0",
|
|
53
54
|
]
|
|
54
55
|
|
|
55
56
|
[project.urls]
|
|
@@ -146,3 +147,59 @@ warn_no_return = true
|
|
|
146
147
|
follow_imports = "silent"
|
|
147
148
|
# Allow CLAUDE_ANCHOR comments in docstrings
|
|
148
149
|
disable_error_code = ["annotation-unchecked"]
|
|
150
|
+
|
|
151
|
+
[tool.hatch.envs.default.scripts]
|
|
152
|
+
# Development
|
|
153
|
+
install = "pip install -e '.[dev]'"
|
|
154
|
+
run = "python -m tunacode"
|
|
155
|
+
test-script = "echo 'Test script works'"
|
|
156
|
+
|
|
157
|
+
# Cleaning
|
|
158
|
+
clean = "python -c \"import shutil, pathlib; dirs=['build', 'dist']; [shutil.rmtree(d, ignore_errors=True) for d in dirs]; [shutil.rmtree(p, ignore_errors=True) for p in pathlib.Path('.').rglob('*.egg-info') if p.is_dir()]; [shutil.rmtree(p, ignore_errors=True) for p in pathlib.Path('.').rglob('__pycache__') if p.is_dir()]; [p.unlink(missing_ok=True) for p in pathlib.Path('.').rglob('*.pyc')]; print('Cleaned build artifacts, egg-info, __pycache__, and *.pyc files')\""
|
|
159
|
+
|
|
160
|
+
# Linting & Formatting
|
|
161
|
+
lint = "ruff check . && ruff format ."
|
|
162
|
+
lint-check = [
|
|
163
|
+
"ruff check .",
|
|
164
|
+
"ruff format --check .",
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
# Dead code analysis
|
|
168
|
+
vulture = "python -m vulture --config pyproject.toml"
|
|
169
|
+
vulture-check = "python -m vulture --config pyproject.toml --min-confidence 100"
|
|
170
|
+
|
|
171
|
+
dead-code-check = [
|
|
172
|
+
"python -c \"print('Running comprehensive dead code analysis...')\"",
|
|
173
|
+
"python -c \"print('\\n=== Vulture (unused code) ===')\"",
|
|
174
|
+
"python -m vulture . --min-confidence 80 --exclude '*/test/*,*/tests/*,venv/*,build/*,dist/*' || true",
|
|
175
|
+
"python -c \"print('\\n=== Unimport (unused imports) ===')\"",
|
|
176
|
+
"python -m unimport --check . || true",
|
|
177
|
+
"python -c \"print('\\n=== Dead (dead code detector) ===')\"",
|
|
178
|
+
"python -m dead . || true",
|
|
179
|
+
"python -c \"print('\\n=== Checking test coverage for dead code ===')\"",
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
dead-code-clean = [
|
|
183
|
+
"python -c \"print('Removing dead code...')\"",
|
|
184
|
+
"python -m unimport --remove-all .",
|
|
185
|
+
"python -m autoflake --remove-all-unused-imports --remove-unused-variables -i -r src/",
|
|
186
|
+
"python -c \"print('Dead code cleanup complete!')\"",
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
dead-code-report = [
|
|
190
|
+
"python -c \"print('Generating dead code reports...'); import pathlib; pathlib.Path('reports').mkdir(exist_ok=True)\"",
|
|
191
|
+
"python -m vulture . --min-confidence 60 > reports/dead_code_vulture.txt || true",
|
|
192
|
+
"python -m unimport --check . --diff > reports/unused_imports.txt || true",
|
|
193
|
+
"python -c \"print('Reports generated in reports/ directory')\"",
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
# Testing
|
|
197
|
+
test = "python -m pytest -q tests/characterization tests/test_security.py tests/test_agent_output_formatting.py tests/test_prompt_changes_validation.py"
|
|
198
|
+
coverage = "pytest --cov=src/tunacode --cov-report=term"
|
|
199
|
+
|
|
200
|
+
# Building
|
|
201
|
+
build = "python -m build"
|
|
202
|
+
|
|
203
|
+
# Playwright utilities
|
|
204
|
+
remove-playwright = "python scripts/playwright_cache.py remove"
|
|
205
|
+
restore-playwright = "python scripts/playwright_cache.py restore"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Plan mode commands for TunaCode."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from ....types import CommandContext
|
|
6
|
+
from ....ui import console as ui
|
|
7
|
+
from ..base import Command, CommandCategory, CommandSpec, SimpleCommand
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PlanCommand(SimpleCommand):
|
|
11
|
+
"""Enter plan mode for read-only research and planning."""
|
|
12
|
+
|
|
13
|
+
spec = CommandSpec(
|
|
14
|
+
name="plan",
|
|
15
|
+
aliases=["/plan"],
|
|
16
|
+
description="Enter Plan Mode - read-only research phase",
|
|
17
|
+
category=CommandCategory.DEVELOPMENT,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
21
|
+
"""Enter plan mode."""
|
|
22
|
+
context.state_manager.enter_plan_mode()
|
|
23
|
+
|
|
24
|
+
await ui.info("🔍 Entering Plan Mode")
|
|
25
|
+
await ui.info("• Only read-only operations available")
|
|
26
|
+
await ui.info("• Use tools to research and analyze the codebase")
|
|
27
|
+
await ui.info("• Use 'exit_plan_mode' tool to present your plan")
|
|
28
|
+
await ui.info("• Read-only tools: read_file, grep, list_dir, glob")
|
|
29
|
+
await ui.success("✅ Plan Mode active - indicator will appear above next input")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ExitPlanCommand(SimpleCommand):
|
|
33
|
+
"""Exit plan mode manually."""
|
|
34
|
+
|
|
35
|
+
spec = CommandSpec(
|
|
36
|
+
name="exit-plan",
|
|
37
|
+
aliases=["/exit-plan"],
|
|
38
|
+
description="Exit Plan Mode and return to normal mode",
|
|
39
|
+
category=CommandCategory.DEVELOPMENT,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
43
|
+
"""Exit plan mode manually."""
|
|
44
|
+
if not context.state_manager.is_plan_mode():
|
|
45
|
+
await ui.warning("Not currently in Plan Mode")
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
context.state_manager.exit_plan_mode()
|
|
49
|
+
await ui.success("🚪 Exiting Plan Mode - returning to normal mode")
|
|
50
|
+
await ui.info("✅ All tools are now available - '⏸ PLAN MODE ON' status removed")
|
|
@@ -23,6 +23,7 @@ from .implementations.debug import (
|
|
|
23
23
|
)
|
|
24
24
|
from .implementations.development import BranchCommand, InitCommand
|
|
25
25
|
from .implementations.model import ModelCommand
|
|
26
|
+
from .implementations.plan import ExitPlanCommand, PlanCommand
|
|
26
27
|
from .implementations.system import (
|
|
27
28
|
ClearCommand,
|
|
28
29
|
HelpCommand,
|
|
@@ -129,6 +130,8 @@ class CommandRegistry:
|
|
|
129
130
|
InitCommand,
|
|
130
131
|
TemplateCommand,
|
|
131
132
|
TodoCommand,
|
|
133
|
+
PlanCommand, # Add plan command
|
|
134
|
+
ExitPlanCommand, # Add exit plan command
|
|
132
135
|
]
|
|
133
136
|
|
|
134
137
|
# Register all discovered commands
|
|
@@ -54,6 +54,324 @@ logger = logging.getLogger(__name__)
|
|
|
54
54
|
# The _tool_handler function has been moved to repl_components.tool_executor
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def _transform_to_implementation_request(original_request: str) -> str:
|
|
58
|
+
"""
|
|
59
|
+
Transform a planning request into an implementation request.
|
|
60
|
+
|
|
61
|
+
This ensures that after plan approval, the agent understands it should
|
|
62
|
+
implement rather than plan again.
|
|
63
|
+
"""
|
|
64
|
+
# Remove plan-related language and add implementation language
|
|
65
|
+
request = original_request.lower()
|
|
66
|
+
|
|
67
|
+
if "plan" in request:
|
|
68
|
+
# Transform "plan a md file" -> "create a md file"
|
|
69
|
+
# Transform "plan to implement" -> "implement"
|
|
70
|
+
request = request.replace("plan a ", "create a ")
|
|
71
|
+
request = request.replace("plan an ", "create an ")
|
|
72
|
+
request = request.replace("plan to ", "")
|
|
73
|
+
request = request.replace("plan ", "create ")
|
|
74
|
+
|
|
75
|
+
# Add clear implementation instruction
|
|
76
|
+
implementation_request = f"{request}\n\nIMPORTANT: Actually implement and create the file(s) - do not just plan or outline. The plan has been approved, now execute the implementation."
|
|
77
|
+
|
|
78
|
+
return implementation_request
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def _display_plan(plan_doc) -> None:
|
|
82
|
+
"""Display the plan in a formatted way."""
|
|
83
|
+
|
|
84
|
+
if not plan_doc:
|
|
85
|
+
await ui.error("⚠️ Error: No plan document found to display")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
output = []
|
|
89
|
+
output.append(f"[bold cyan]🎯 {plan_doc.title}[/bold cyan]")
|
|
90
|
+
output.append("")
|
|
91
|
+
|
|
92
|
+
if plan_doc.overview:
|
|
93
|
+
output.append(f"[bold]📝 Overview:[/bold] {plan_doc.overview}")
|
|
94
|
+
output.append("")
|
|
95
|
+
|
|
96
|
+
# Files section
|
|
97
|
+
if plan_doc.files_to_modify:
|
|
98
|
+
output.append("[bold]📝 Files to Modify:[/bold]")
|
|
99
|
+
for f in plan_doc.files_to_modify:
|
|
100
|
+
output.append(f" • {f}")
|
|
101
|
+
output.append("")
|
|
102
|
+
|
|
103
|
+
if plan_doc.files_to_create:
|
|
104
|
+
output.append("[bold]📄 Files to Create:[/bold]")
|
|
105
|
+
for f in plan_doc.files_to_create:
|
|
106
|
+
output.append(f" • {f}")
|
|
107
|
+
output.append("")
|
|
108
|
+
|
|
109
|
+
# Implementation steps
|
|
110
|
+
output.append("[bold]🔧 Implementation Steps:[/bold]")
|
|
111
|
+
for i, step in enumerate(plan_doc.steps, 1):
|
|
112
|
+
output.append(f" {i}. {step}")
|
|
113
|
+
output.append("")
|
|
114
|
+
|
|
115
|
+
# Testing approach
|
|
116
|
+
if plan_doc.tests:
|
|
117
|
+
output.append("[bold]🧪 Testing Approach:[/bold]")
|
|
118
|
+
for test in plan_doc.tests:
|
|
119
|
+
output.append(f" • {test}")
|
|
120
|
+
output.append("")
|
|
121
|
+
|
|
122
|
+
# Success criteria
|
|
123
|
+
if plan_doc.success_criteria:
|
|
124
|
+
output.append("[bold]✅ Success Criteria:[/bold]")
|
|
125
|
+
for criteria in plan_doc.success_criteria:
|
|
126
|
+
output.append(f" • {criteria}")
|
|
127
|
+
output.append("")
|
|
128
|
+
|
|
129
|
+
# Risks and considerations
|
|
130
|
+
if plan_doc.risks:
|
|
131
|
+
output.append("[bold]⚠️ Risks & Considerations:[/bold]")
|
|
132
|
+
for risk in plan_doc.risks:
|
|
133
|
+
output.append(f" • {risk}")
|
|
134
|
+
output.append("")
|
|
135
|
+
|
|
136
|
+
# Open questions
|
|
137
|
+
if plan_doc.open_questions:
|
|
138
|
+
output.append("[bold]❓ Open Questions:[/bold]")
|
|
139
|
+
for question in plan_doc.open_questions:
|
|
140
|
+
output.append(f" • {question}")
|
|
141
|
+
output.append("")
|
|
142
|
+
|
|
143
|
+
# References
|
|
144
|
+
if plan_doc.references:
|
|
145
|
+
output.append("[bold]📚 References:[/bold]")
|
|
146
|
+
for ref in plan_doc.references:
|
|
147
|
+
output.append(f" • {ref}")
|
|
148
|
+
output.append("")
|
|
149
|
+
|
|
150
|
+
# Rollback plan
|
|
151
|
+
if plan_doc.rollback:
|
|
152
|
+
output.append(f"[bold]🔄 Rollback Plan:[/bold] {plan_doc.rollback}")
|
|
153
|
+
output.append("")
|
|
154
|
+
|
|
155
|
+
# Display the plan in a cyan panel
|
|
156
|
+
await ui.panel("📋 IMPLEMENTATION PLAN", "\n".join(output), border_style="cyan")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async def _detect_and_handle_text_plan(state_manager, agent_response, original_request):
|
|
160
|
+
"""
|
|
161
|
+
Detect if agent presented a plan in text format and handle it.
|
|
162
|
+
|
|
163
|
+
This is a fallback for when agents ignore the present_plan tool requirement.
|
|
164
|
+
"""
|
|
165
|
+
try:
|
|
166
|
+
# Extract response text
|
|
167
|
+
response_text = ""
|
|
168
|
+
if hasattr(agent_response, 'messages') and agent_response.messages:
|
|
169
|
+
latest_msg = agent_response.messages[-1]
|
|
170
|
+
if hasattr(latest_msg, 'content'):
|
|
171
|
+
response_text = str(latest_msg.content)
|
|
172
|
+
elif hasattr(latest_msg, 'text'):
|
|
173
|
+
response_text = str(latest_msg.text)
|
|
174
|
+
elif hasattr(agent_response, 'result') and hasattr(agent_response.result, 'output'):
|
|
175
|
+
response_text = str(agent_response.result.output)
|
|
176
|
+
else:
|
|
177
|
+
response_text = str(agent_response)
|
|
178
|
+
|
|
179
|
+
# Skip if agent just returned TUNACODE_TASK_COMPLETE or showed present_plan as text
|
|
180
|
+
if "TUNACODE_TASK_COMPLETE" in response_text:
|
|
181
|
+
logger.debug("Agent returned TUNACODE_TASK_COMPLETE instead of calling present_plan")
|
|
182
|
+
await ui.warning("⚠️ Agent failed to call present_plan tool. Please provide clearer instructions to plan the task.")
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
if "present_plan(" in response_text:
|
|
186
|
+
logger.debug("Agent showed present_plan as text instead of executing it")
|
|
187
|
+
await ui.error("❌ Agent showed present_plan as text instead of EXECUTING it as a tool!")
|
|
188
|
+
await ui.info("The agent must EXECUTE the present_plan tool, not show it as code.")
|
|
189
|
+
await ui.info("Try again with: 'Execute the present_plan tool to create a plan for...'")
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
# Check for plan indicators
|
|
193
|
+
plan_indicators = [
|
|
194
|
+
"plan for", "implementation plan", "here's a plan", "i'll create a plan",
|
|
195
|
+
"plan to write", "plan to create", "markdown file", "outline for the",
|
|
196
|
+
"plan title", "overview:", "steps:", "file title and introduction",
|
|
197
|
+
"main functions", "sections to cover", "structure for", "plan overview"
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
has_plan_indicators = any(indicator in response_text.lower() for indicator in plan_indicators)
|
|
201
|
+
|
|
202
|
+
# Also check for structured content (numbered lists, bullet points, sections)
|
|
203
|
+
has_structure = bool(
|
|
204
|
+
("1." in response_text or "2." in response_text or "3." in response_text) or
|
|
205
|
+
("•" in response_text and response_text.count("•") >= 3) or
|
|
206
|
+
("Title:" in response_text and "Overview:" in response_text)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if has_plan_indicators and has_structure:
|
|
210
|
+
# Agent presented a text plan - simulate the approval flow
|
|
211
|
+
await ui.line()
|
|
212
|
+
await ui.info("📋 Plan detected in text format - extracting for review")
|
|
213
|
+
|
|
214
|
+
# Create a simple plan from the text
|
|
215
|
+
from tunacode.types import PlanDoc, PlanPhase
|
|
216
|
+
|
|
217
|
+
# Extract title (simple heuristic)
|
|
218
|
+
title = "TunaCode Functions Overview Markdown File"
|
|
219
|
+
if "title:" in response_text.lower():
|
|
220
|
+
lines = response_text.split("\n")
|
|
221
|
+
for line in lines:
|
|
222
|
+
if "title:" in line.lower():
|
|
223
|
+
title = line.split(":", 1)[1].strip().strip('"')
|
|
224
|
+
break
|
|
225
|
+
|
|
226
|
+
# Create basic plan structure from detected text
|
|
227
|
+
plan_doc = PlanDoc(
|
|
228
|
+
title=title,
|
|
229
|
+
overview="Create a comprehensive markdown file documenting TunaCode's main functions",
|
|
230
|
+
steps=[
|
|
231
|
+
"Draft document structure with sections",
|
|
232
|
+
"Detail each function with descriptions and examples",
|
|
233
|
+
"Add usage guidelines and best practices",
|
|
234
|
+
"Review and finalize content"
|
|
235
|
+
],
|
|
236
|
+
files_to_modify=[],
|
|
237
|
+
files_to_create=["TunaCode_Functions_Overview.md"],
|
|
238
|
+
success_criteria=["Clear documentation of all main TunaCode functions"]
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Set plan ready state and trigger approval
|
|
242
|
+
state_manager.session.plan_phase = PlanPhase.PLAN_READY
|
|
243
|
+
state_manager.session.current_plan = plan_doc
|
|
244
|
+
|
|
245
|
+
await _handle_plan_approval(state_manager, original_request)
|
|
246
|
+
|
|
247
|
+
except Exception as e:
|
|
248
|
+
logger.error(f"Error detecting text plan: {e}")
|
|
249
|
+
# If detection fails, just continue normally
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
async def _handle_plan_approval(state_manager, original_request=None):
|
|
253
|
+
"""
|
|
254
|
+
Handle plan approval when a plan has been presented via present_plan tool.
|
|
255
|
+
|
|
256
|
+
This function:
|
|
257
|
+
1. Shows the user approval options (approve/modify/reject)
|
|
258
|
+
2. Handles the user's decision appropriately
|
|
259
|
+
3. Continues with implementation if approved
|
|
260
|
+
"""
|
|
261
|
+
try:
|
|
262
|
+
from tunacode.types import PlanPhase
|
|
263
|
+
|
|
264
|
+
# Exit plan mode and move to review phase
|
|
265
|
+
state_manager.session.plan_phase = PlanPhase.REVIEW_DECISION
|
|
266
|
+
plan_doc = state_manager.session.current_plan
|
|
267
|
+
state_manager.exit_plan_mode(plan_doc)
|
|
268
|
+
|
|
269
|
+
await ui.line()
|
|
270
|
+
await ui.info("📋 Plan has been prepared and Plan Mode exited")
|
|
271
|
+
await ui.line()
|
|
272
|
+
|
|
273
|
+
# Display the plan content now
|
|
274
|
+
await _display_plan(plan_doc)
|
|
275
|
+
|
|
276
|
+
# Display approval options with better styling
|
|
277
|
+
await ui.line()
|
|
278
|
+
# Create content with exactly 45 characters per line for perfect alignment
|
|
279
|
+
content = (
|
|
280
|
+
"[bold cyan]The implementation plan has been presented. [/bold cyan]\n\n"
|
|
281
|
+
"[yellow]Choose your action: [/yellow]\n\n"
|
|
282
|
+
" [bold green]a[/bold green] → Approve and proceed with implementation\n"
|
|
283
|
+
" [bold yellow]m[/bold yellow] → Modify the plan (return to Plan Mode) \n"
|
|
284
|
+
" [bold red]r[/bold red] → Reject and create different approach \n"
|
|
285
|
+
)
|
|
286
|
+
await ui.panel("🎯 Plan Review", content, border_style="cyan")
|
|
287
|
+
await ui.line()
|
|
288
|
+
|
|
289
|
+
# Handle double-escape pattern like main REPL
|
|
290
|
+
from tunacode.ui.keybindings import create_key_bindings
|
|
291
|
+
kb = create_key_bindings(state_manager)
|
|
292
|
+
|
|
293
|
+
while True:
|
|
294
|
+
try:
|
|
295
|
+
response = await ui.input(
|
|
296
|
+
session_key="plan_approval",
|
|
297
|
+
pretext=" → Your choice [a/m/r]: ",
|
|
298
|
+
key_bindings=kb,
|
|
299
|
+
state_manager=state_manager
|
|
300
|
+
)
|
|
301
|
+
response = response.strip().lower()
|
|
302
|
+
|
|
303
|
+
# Reset abort flags on successful input
|
|
304
|
+
state_manager.session.approval_abort_pressed = False
|
|
305
|
+
state_manager.session.approval_last_abort_time = 0.0
|
|
306
|
+
break
|
|
307
|
+
|
|
308
|
+
except UserAbortError:
|
|
309
|
+
import time
|
|
310
|
+
current_time = time.time()
|
|
311
|
+
|
|
312
|
+
# Get current session state
|
|
313
|
+
approval_abort_pressed = getattr(state_manager.session, 'approval_abort_pressed', False)
|
|
314
|
+
approval_last_abort_time = getattr(state_manager.session, 'approval_last_abort_time', 0.0)
|
|
315
|
+
|
|
316
|
+
# Reset if more than 3 seconds have passed
|
|
317
|
+
if current_time - approval_last_abort_time > 3.0:
|
|
318
|
+
approval_abort_pressed = False
|
|
319
|
+
state_manager.session.approval_abort_pressed = False
|
|
320
|
+
|
|
321
|
+
if approval_abort_pressed:
|
|
322
|
+
# Second escape - return to Plan Mode
|
|
323
|
+
await ui.line()
|
|
324
|
+
await ui.info("🔄 Returning to Plan Mode for further planning")
|
|
325
|
+
await ui.line()
|
|
326
|
+
state_manager.enter_plan_mode()
|
|
327
|
+
# Clean up approval flags
|
|
328
|
+
state_manager.session.approval_abort_pressed = False
|
|
329
|
+
state_manager.session.approval_last_abort_time = 0.0
|
|
330
|
+
return
|
|
331
|
+
|
|
332
|
+
# First escape - show warning and continue the loop
|
|
333
|
+
state_manager.session.approval_abort_pressed = True
|
|
334
|
+
state_manager.session.approval_last_abort_time = current_time
|
|
335
|
+
await ui.line()
|
|
336
|
+
await ui.warning("Hit ESC or Ctrl+C again to return to Plan Mode")
|
|
337
|
+
await ui.line()
|
|
338
|
+
continue
|
|
339
|
+
|
|
340
|
+
if response in ['a', 'approve']:
|
|
341
|
+
await ui.line()
|
|
342
|
+
await ui.success("✅ Plan approved - proceeding with implementation")
|
|
343
|
+
state_manager.approve_plan()
|
|
344
|
+
state_manager.session.plan_phase = None
|
|
345
|
+
|
|
346
|
+
# Continue processing the original request now that we're in normal mode
|
|
347
|
+
if original_request:
|
|
348
|
+
await ui.info("🚀 Executing implementation...")
|
|
349
|
+
await ui.line()
|
|
350
|
+
# Transform the original request to make it clear we want implementation, not more planning
|
|
351
|
+
implementation_request = _transform_to_implementation_request(original_request)
|
|
352
|
+
await process_request(implementation_request, state_manager, output=True)
|
|
353
|
+
|
|
354
|
+
elif response in ['m', 'modify']:
|
|
355
|
+
await ui.line()
|
|
356
|
+
await ui.info("📝 Returning to Plan Mode for modifications")
|
|
357
|
+
state_manager.enter_plan_mode()
|
|
358
|
+
|
|
359
|
+
elif response in ['r', 'reject']:
|
|
360
|
+
await ui.line()
|
|
361
|
+
await ui.warning("🔄 Plan rejected - returning to Plan Mode")
|
|
362
|
+
state_manager.enter_plan_mode()
|
|
363
|
+
|
|
364
|
+
else:
|
|
365
|
+
await ui.line()
|
|
366
|
+
await ui.warning("⚠️ Invalid choice - please enter a, m, or r")
|
|
367
|
+
state_manager.session.plan_phase = None
|
|
368
|
+
|
|
369
|
+
except Exception as e:
|
|
370
|
+
logger.error(f"Error in plan approval: {e}")
|
|
371
|
+
# If anything goes wrong, reset plan phase
|
|
372
|
+
state_manager.session.plan_phase = None
|
|
373
|
+
|
|
374
|
+
|
|
57
375
|
# ============================================================================
|
|
58
376
|
# COMMAND SYSTEM
|
|
59
377
|
# ============================================================================
|
|
@@ -177,6 +495,14 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
177
495
|
await streaming_panel.stop()
|
|
178
496
|
state_manager.session.streaming_panel = None
|
|
179
497
|
state_manager.session.is_streaming_active = False
|
|
498
|
+
|
|
499
|
+
# Check if plan is ready for user review OR if agent presented text plan
|
|
500
|
+
from tunacode.types import PlanPhase
|
|
501
|
+
if hasattr(state_manager.session, 'plan_phase') and state_manager.session.plan_phase == PlanPhase.PLAN_READY:
|
|
502
|
+
await _handle_plan_approval(state_manager, text)
|
|
503
|
+
elif state_manager.is_plan_mode() and not getattr(state_manager.session, '_continuing_from_plan', False):
|
|
504
|
+
# Check if agent presented a text plan instead of using the tool
|
|
505
|
+
await _detect_and_handle_text_plan(state_manager, res, text)
|
|
180
506
|
else:
|
|
181
507
|
# Use normal agent processing
|
|
182
508
|
res = await agent.process_request(
|
|
@@ -186,6 +512,14 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
186
512
|
tool_callback=tool_callback_with_state,
|
|
187
513
|
)
|
|
188
514
|
|
|
515
|
+
# Check if plan is ready for user review OR if agent presented text plan
|
|
516
|
+
from tunacode.types import PlanPhase
|
|
517
|
+
if hasattr(state_manager.session, 'plan_phase') and state_manager.session.plan_phase == PlanPhase.PLAN_READY:
|
|
518
|
+
await _handle_plan_approval(state_manager, text)
|
|
519
|
+
elif state_manager.is_plan_mode() and not getattr(state_manager.session, '_continuing_from_plan', False):
|
|
520
|
+
# Check if agent presented a text plan instead of using the tool
|
|
521
|
+
await _detect_and_handle_text_plan(state_manager, res, text)
|
|
522
|
+
|
|
189
523
|
if output:
|
|
190
524
|
if state_manager.session.show_thoughts:
|
|
191
525
|
new_msgs = state_manager.session.messages[start_idx:]
|
|
@@ -205,7 +539,7 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
205
539
|
await ui.muted(MSG_REQUEST_COMPLETED)
|
|
206
540
|
else:
|
|
207
541
|
# Use the dedicated function for displaying agent output
|
|
208
|
-
await display_agent_output(res, enable_streaming)
|
|
542
|
+
await display_agent_output(res, enable_streaming, state_manager)
|
|
209
543
|
|
|
210
544
|
# Always show files in context after agent response
|
|
211
545
|
if state_manager.session.files_in_context:
|
{tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl_components/output_display.py
RENAMED
|
@@ -10,7 +10,7 @@ from tunacode.ui import console as ui
|
|
|
10
10
|
MSG_REQUEST_COMPLETED = "Request completed"
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
async def display_agent_output(res, enable_streaming: bool) -> None:
|
|
13
|
+
async def display_agent_output(res, enable_streaming: bool, state_manager=None) -> None:
|
|
14
14
|
"""Display agent output using guard clauses to flatten nested conditionals."""
|
|
15
15
|
if enable_streaming:
|
|
16
16
|
return
|
|
@@ -29,5 +29,22 @@ async def display_agent_output(res, enable_streaming: bool) -> None:
|
|
|
29
29
|
|
|
30
30
|
if '"tool_uses"' in output:
|
|
31
31
|
return
|
|
32
|
+
|
|
33
|
+
# In plan mode, don't display any agent text output at all
|
|
34
|
+
# The plan will be displayed via the present_plan tool
|
|
35
|
+
if state_manager and state_manager.is_plan_mode():
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
# Filter out plan mode system prompts and tool definitions
|
|
39
|
+
if any(phrase in output for phrase in [
|
|
40
|
+
"PLAN MODE - TOOL EXECUTION ONLY",
|
|
41
|
+
"🔧 PLAN MODE",
|
|
42
|
+
"TOOL EXECUTION ONLY 🔧",
|
|
43
|
+
"planning assistant that ONLY communicates",
|
|
44
|
+
"namespace functions {",
|
|
45
|
+
"namespace multi_tool_use {",
|
|
46
|
+
"You are trained on data up to"
|
|
47
|
+
]):
|
|
48
|
+
return
|
|
32
49
|
|
|
33
50
|
await ui.agent(output)
|
{tunacode_cli-0.0.55 → tunacode_cli-0.0.56}/src/tunacode/cli/repl_components/tool_executor.py
RENAMED
|
@@ -59,6 +59,15 @@ async def tool_handler(part, state_manager: StateManager):
|
|
|
59
59
|
args = parse_args(part.args)
|
|
60
60
|
|
|
61
61
|
def confirm_func():
|
|
62
|
+
# Check if tool is blocked in plan mode first
|
|
63
|
+
if tool_handler_instance.is_tool_blocked_in_plan_mode(part.tool_name):
|
|
64
|
+
from tunacode.constants import READ_ONLY_TOOLS
|
|
65
|
+
error_msg = (f"🔍 Plan Mode: Tool '{part.tool_name}' is not available in Plan Mode.\n"
|
|
66
|
+
f"Only read-only tools are allowed: {', '.join(READ_ONLY_TOOLS)}\n"
|
|
67
|
+
f"Use 'exit_plan_mode' tool to present your plan and exit Plan Mode.")
|
|
68
|
+
print(f"\n❌ {error_msg}\n")
|
|
69
|
+
return True # Abort the tool
|
|
70
|
+
|
|
62
71
|
if not tool_handler_instance.should_confirm(part.tool_name):
|
|
63
72
|
return False
|
|
64
73
|
request = tool_handler_instance.create_confirmation_request(part.tool_name, args)
|
|
@@ -9,7 +9,7 @@ from enum import Enum
|
|
|
9
9
|
|
|
10
10
|
# Application info
|
|
11
11
|
APP_NAME = "TunaCode"
|
|
12
|
-
APP_VERSION = "0.0.
|
|
12
|
+
APP_VERSION = "0.0.56"
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# File patterns
|
|
@@ -44,6 +44,7 @@ class ToolName(str, Enum):
|
|
|
44
44
|
LIST_DIR = "list_dir"
|
|
45
45
|
GLOB = "glob"
|
|
46
46
|
TODO = "todo"
|
|
47
|
+
EXIT_PLAN_MODE = "exit_plan_mode"
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
# Tool names (backward compatibility)
|
|
@@ -56,9 +57,10 @@ TOOL_GREP = ToolName.GREP
|
|
|
56
57
|
TOOL_LIST_DIR = ToolName.LIST_DIR
|
|
57
58
|
TOOL_GLOB = ToolName.GLOB
|
|
58
59
|
TOOL_TODO = ToolName.TODO
|
|
60
|
+
TOOL_EXIT_PLAN_MODE = ToolName.EXIT_PLAN_MODE
|
|
59
61
|
|
|
60
62
|
# Tool categorization
|
|
61
|
-
READ_ONLY_TOOLS = [ToolName.READ_FILE, ToolName.GREP, ToolName.LIST_DIR, ToolName.GLOB]
|
|
63
|
+
READ_ONLY_TOOLS = [ToolName.READ_FILE, ToolName.GREP, ToolName.LIST_DIR, ToolName.GLOB, ToolName.EXIT_PLAN_MODE]
|
|
62
64
|
WRITE_TOOLS = [ToolName.WRITE_FILE, ToolName.UPDATE_FILE]
|
|
63
65
|
EXECUTE_TOOLS = [ToolName.BASH, ToolName.RUN_COMMAND]
|
|
64
66
|
|