cafe-engine 0.1.2__tar.gz → 0.1.3__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.
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/CHANGELOG.md +11 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/PKG-INFO +1 -1
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/pyproject.toml +1 -1
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/cli/copilot.py +5 -5
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/phase.py +9 -2
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/phases/pr_phase.py +75 -23
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/cli.py +27 -1
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/checklist_generator.py +8 -1
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/checklist_templates.py +2 -2
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_pr_command.py +87 -1
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_prepare.py +186 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_copilot_cli.py +43 -0
- cafe_engine-0.1.3/tests/unit/test_pr_phase_output_todo_only.py +194 -0
- cafe_engine-0.1.3/tests/unit/test_pr_phase_post_todo_comment.py +317 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/uv.lock +1 -1
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/.claude/settings.local.json +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/.geminiignore +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/.githooks/pre-commit +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/.gitignore +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/CONTRIBUTING.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/LICENSE +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/README.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/images/cafe-flow.png +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/scripts/simulate_agent_test.sh +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/setup-hooks.sh +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/cli/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/cli/abstract.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/cli/claude.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/cli/cursor.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/cli/gemini.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/executor.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/manager.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/agents/mock_executor.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/git.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/permission.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/phase_cache.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/questions_schema.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/session.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/status_codes.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/core/types.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/developer/David.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/developer/Nick.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/developer//347/276/205/345/215/232/351/253/230.md" +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/developer//351/255/257/346/237/224/345/207/241.md" +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/pm/Roger.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/pm//350/214/203/346/233/231/347/207/201.md" +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/reviewer/Richard.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/agents/reviewer//346/236/227/350/220/214/350/212/235.md" +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/templates/plan/bug.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/templates/plan/default.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/templates/plan/simple.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/templates/spec/default.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/templates/spec/detailed.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/data/templates/spec/simple.md +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/phases/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/phases/develop_phase.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/phases/plan_phase.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/phases/review_phase.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/phases/spec_phase.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/services/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/services/delta_display.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/services/summary_display.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/services/summary_service.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/services/time_formatter.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/services/timeline_builder.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/templates/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/templates/manager.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/display.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/init_helpers.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/inquirer_prompts.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/interactive_qa.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/phase_prompts.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/ui/template_selector.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/checklist_utils.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/checklist_validator.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/config.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/git_utils.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/github.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/src/cafe/utils/prompt_utils.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/.gitignore +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/conftest.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/fixtures/minimal_config.yaml +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/conftest.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/test_gemini_session_integration.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/test_global_config_integration.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/test_pr_command_non_interactive.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/test_pr_e2e_with_mock.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/test_prepare_non_github.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/integration/test_spec_interactive_qa_e2e.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/__init__.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_abstract_cli.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_agent_edit_auto_sync.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_agent_executor.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_agent_executor_path_formats.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_agent_loading.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_agent_manager.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_agent_manager_mock_mode.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_check_precommit_env.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_checklist_generator.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_checklist_utils.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_claude_cli.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_agent_commands.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_agent_defaults.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_agent_sync.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_auto_mode.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_close.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_close_pr_mode.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_init.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_init_phase_config.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_invoke_command.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_ls_rm.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_make.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_module_init.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_phase_commands.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_prepare_init.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_restore.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_setup_agents.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_show.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_spec_auto.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_spec_output.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_template.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cli_template_sync.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_config.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_context_endtime.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_context_session_id_update.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_copilot_parse_response.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_cursor_cli.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_delta_display.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_develop_clarification.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_develop_no_changes_needed.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_develop_phase_prompt.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_directory_structure.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_display.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_ensure_agent_file_exists.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_ensure_default_content.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_executor_session_recovery_generic.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_gemini_cli.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_gemini_content_extractor.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_git.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_git_utils.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_git_utils_ceiling.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_git_utils_repo_root.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_git_worktree.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_github.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_github_pr_comments.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_github_utils.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_global_config.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_image_download.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_init_helpers.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_inquirer_prompts.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_interactive_qa.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_mock_agent_executor.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_permission.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_cache.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_get_issue_config_value.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_interactive.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_iteration_structure.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_non_interactive_behavior.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_progress.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_prompts.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_phase_status_analysis_with_tools.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_plan_phase_github_sync.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_plan_phase_status_codes.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_plan_phase_sync_github_config.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_plan_phase_template_mode.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_comment_images.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_context.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_generate_content.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_github_mode.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_issue_comment.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_iteration_logic.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_parsing.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_prepare_content.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_pr_phase_status_analysis.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_prepare_auto_sync.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_prompt_utils.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_questions_schema.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_reset_command.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_resolve_sync_github.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_session.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_spec_github_sync.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_spec_image_download.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_spec_phase_qa.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_spec_phase_sync_github_config.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_spec_prompt_with_images.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_status_codes.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_summary_display.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_summary_service.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_sync_helpers.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_template_edit_auto_sync.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_template_manager.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_template_manager_global.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_template_selector_auto.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_time_formatter.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_timeline_builder.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/test_types.py +0 -0
- {cafe_engine-0.1.2 → cafe_engine-0.1.3}/tests/unit/utils/test_checklist_validator.py +0 -0
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.1.3]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Added `--post-todo-list` option to `cafe pr` command to post PR comment todo list as GitHub PR comment
|
|
9
|
+
- Added `--post-pr-todo-list` option to `cafe prepare` command to set post-PR-todo-list mode for prepare phase
|
|
10
|
+
- Added token usage summary display after PR comment organization completes
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Fixed copilot session_id detection to recognize both file-based (.jsonl) and directory-based session formats
|
|
14
|
+
- Fixed `_print_token_usage_summary` to gracefully handle mocked agent_manager in tests
|
|
15
|
+
|
|
5
16
|
## [0.1.2]
|
|
6
17
|
|
|
7
18
|
### Fixed
|
|
@@ -221,7 +221,7 @@ class CopilotCLI(AbstractCLI):
|
|
|
221
221
|
|
|
222
222
|
if copilot_session_dir.exists():
|
|
223
223
|
self._existing_sessions = {
|
|
224
|
-
f.name for f in copilot_session_dir.iterdir() if f.is_file()
|
|
224
|
+
f.name for f in copilot_session_dir.iterdir() if f.is_file() or f.is_dir()
|
|
225
225
|
}
|
|
226
226
|
else:
|
|
227
227
|
self._existing_sessions = set()
|
|
@@ -229,7 +229,7 @@ class CopilotCLI(AbstractCLI):
|
|
|
229
229
|
def extract_session_id(self, output_lines: List[str]) -> Optional[str]:
|
|
230
230
|
"""Extract newly created session ID from file system.
|
|
231
231
|
|
|
232
|
-
Copilot automatically creates session
|
|
232
|
+
Copilot automatically creates session directories, need to detect from file system.
|
|
233
233
|
|
|
234
234
|
Args:
|
|
235
235
|
output_lines: List of lines from CLI output (not used here)
|
|
@@ -245,9 +245,9 @@ class CopilotCLI(AbstractCLI):
|
|
|
245
245
|
# Wait for file system to update
|
|
246
246
|
time.sleep(0.1)
|
|
247
247
|
|
|
248
|
-
# Get current session files
|
|
248
|
+
# Get current session files and directories
|
|
249
249
|
current_sessions = {
|
|
250
|
-
f.name for f in copilot_session_dir.iterdir() if f.is_file()
|
|
250
|
+
f.name for f in copilot_session_dir.iterdir() if f.is_file() or f.is_dir()
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
# Find newly created sessions
|
|
@@ -256,7 +256,7 @@ class CopilotCLI(AbstractCLI):
|
|
|
256
256
|
if new_sessions:
|
|
257
257
|
# Get newest session (sorted by name)
|
|
258
258
|
newest_session = sorted(new_sessions)[-1]
|
|
259
|
-
# Remove .jsonl extension
|
|
259
|
+
# Remove .jsonl extension if present (for file-based sessions)
|
|
260
260
|
session_id = newest_session.replace(".jsonl", "")
|
|
261
261
|
return session_id
|
|
262
262
|
|
|
@@ -1136,8 +1136,15 @@ class Phase(ABC):
|
|
|
1136
1136
|
if not hasattr(self, "agent_manager"):
|
|
1137
1137
|
return
|
|
1138
1138
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1139
|
+
try:
|
|
1140
|
+
token_usage = self.agent_manager.get_total_token_usage()
|
|
1141
|
+
model = self.agent_manager.get_last_model()
|
|
1142
|
+
|
|
1143
|
+
# Verify we have real token usage data (not mocks)
|
|
1144
|
+
if not isinstance(token_usage.input_tokens, int):
|
|
1145
|
+
return
|
|
1146
|
+
except (AttributeError, TypeError):
|
|
1147
|
+
return
|
|
1141
1148
|
|
|
1142
1149
|
print()
|
|
1143
1150
|
print("=" * 60)
|
|
@@ -38,6 +38,7 @@ class PRPhase(Phase):
|
|
|
38
38
|
force_push: bool = False,
|
|
39
39
|
interactive: bool = True,
|
|
40
40
|
base_branch: Optional[str] = None,
|
|
41
|
+
post_todo_list: Optional[bool] = None,
|
|
41
42
|
) -> None:
|
|
42
43
|
"""Initialize PR phase.
|
|
43
44
|
|
|
@@ -57,6 +58,7 @@ class PRPhase(Phase):
|
|
|
57
58
|
force_push: Force push to remote (default: False)
|
|
58
59
|
interactive: Enable interactive mode (default: True)
|
|
59
60
|
base_branch: Target base branch (None for auto-detection from config)
|
|
61
|
+
post_todo_list: Post organized todo list as PR comment (None for auto-detect from config)
|
|
60
62
|
"""
|
|
61
63
|
super().__init__(interactive=interactive)
|
|
62
64
|
|
|
@@ -93,6 +95,19 @@ class PRPhase(Phase):
|
|
|
93
95
|
config_base = self._get_issue_config_value(config_file, "base_branch")
|
|
94
96
|
self.base_branch = config_base if config_base else "main"
|
|
95
97
|
|
|
98
|
+
# Resolve post_todo_list: CLI value > config file value > default (True)
|
|
99
|
+
if post_todo_list is not None:
|
|
100
|
+
# CLI parameter takes precedence
|
|
101
|
+
self.post_todo_list = post_todo_list
|
|
102
|
+
else:
|
|
103
|
+
config_file = self.issue_dir / "issue.yaml"
|
|
104
|
+
config_value = self._get_issue_config_value(config_file, "pr.post_todo_list")
|
|
105
|
+
if config_value is not None:
|
|
106
|
+
self.post_todo_list = bool(config_value)
|
|
107
|
+
else:
|
|
108
|
+
# Default to True to maintain backward compatibility
|
|
109
|
+
self.post_todo_list = True
|
|
110
|
+
|
|
96
111
|
# Set up history tracking (like other phases)
|
|
97
112
|
self.phase_dir = self.issue_dir / "pr"
|
|
98
113
|
self.history_dir = self.phase_dir / "history"
|
|
@@ -1078,6 +1093,21 @@ class PRPhase(Phase):
|
|
|
1078
1093
|
console.print(f"[yellow]⚠️ Warning: Failed to fetch/save PR comments: {e}[/yellow]")
|
|
1079
1094
|
return None
|
|
1080
1095
|
|
|
1096
|
+
@staticmethod
|
|
1097
|
+
def _build_todo_list_comment(todo_content: str, user_input_path: str) -> str:
|
|
1098
|
+
"""Build PR comment body with todo list and user_input.md reference.
|
|
1099
|
+
|
|
1100
|
+
Args:
|
|
1101
|
+
todo_content: The organized todo list content
|
|
1102
|
+
user_input_path: File path to user_input.md for reference
|
|
1103
|
+
|
|
1104
|
+
Returns:
|
|
1105
|
+
Formatted comment body with file path reference and todo list
|
|
1106
|
+
"""
|
|
1107
|
+
return f"""> 📋 Original review comments: `{user_input_path}`
|
|
1108
|
+
|
|
1109
|
+
{todo_content}"""
|
|
1110
|
+
|
|
1081
1111
|
def _organize_comments_to_todo_list(self, pr_number: int, pr_url: str, branch_name: str) -> PhaseResult:
|
|
1082
1112
|
"""Organize PR comments into actionable todo list format.
|
|
1083
1113
|
|
|
@@ -1139,13 +1169,13 @@ class PRPhase(Phase):
|
|
|
1139
1169
|
output_file_path=output_pattern,
|
|
1140
1170
|
prev_output_file_path=prev_output_pattern,
|
|
1141
1171
|
checklist_file_path=checklist_file,
|
|
1172
|
+
basic_principles=None,
|
|
1142
1173
|
)
|
|
1143
1174
|
|
|
1144
|
-
#
|
|
1145
|
-
#
|
|
1146
|
-
if
|
|
1147
|
-
|
|
1148
|
-
shutil.copy2(user_input_file, output_file)
|
|
1175
|
+
# Create empty output.md file for agent to write todo list only
|
|
1176
|
+
# Original PR comments remain in user_input.md for reference
|
|
1177
|
+
if not output_file.exists():
|
|
1178
|
+
output_file.write_text("", encoding="utf-8")
|
|
1149
1179
|
|
|
1150
1180
|
# Get relative paths for tool permissions
|
|
1151
1181
|
try:
|
|
@@ -1202,16 +1232,11 @@ Do NOT return a status code until ALL checklist items are marked as [x].
|
|
|
1202
1232
|
**Task:** Organize PR comments into actionable todo list.
|
|
1203
1233
|
|
|
1204
1234
|
**Context:**
|
|
1205
|
-
-
|
|
1206
|
-
|
|
1207
|
-
**Instructions:**
|
|
1208
|
-
1. Read the output file which already contains the PR comments
|
|
1209
|
-
2. Analyze the comments and organize them into actionable todo items
|
|
1210
|
-
3. Append the todo list to the END of the output file (do not remove the original content)
|
|
1235
|
+
- PR comments source: {user_input_pattern} (original review comments)
|
|
1236
|
+
- Output file: {output_pattern} (write organized todo list here){images_instruction}
|
|
1211
1237
|
|
|
1212
|
-
**Output format to
|
|
1238
|
+
**Output format to write:**
|
|
1213
1239
|
```markdown
|
|
1214
|
-
|
|
1215
1240
|
## Todo List
|
|
1216
1241
|
|
|
1217
1242
|
### [Category/Theme]
|
|
@@ -1268,18 +1293,24 @@ The system will verify checklist completion. If unchecked items remain, you will
|
|
|
1268
1293
|
message=f"Checklist validation failed - {validation_result.unchecked_count} items not marked as complete",
|
|
1269
1294
|
)
|
|
1270
1295
|
|
|
1271
|
-
# Validate output.md
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
)
|
|
1279
|
-
if
|
|
1296
|
+
# Validate output.md contains todo list content
|
|
1297
|
+
if not output_file.exists():
|
|
1298
|
+
return PhaseResult(
|
|
1299
|
+
status=PhaseStatus.FAILED,
|
|
1300
|
+
message="Agent did not create output.md file",
|
|
1301
|
+
)
|
|
1302
|
+
|
|
1303
|
+
output_content = output_file.read_text(encoding="utf-8")
|
|
1304
|
+
# Check if output.md contains todo list markers (either ## Todo List or checkbox items)
|
|
1305
|
+
has_todo_list = ("## Todo List" in output_content or
|
|
1306
|
+
"## Todo" in output_content or
|
|
1307
|
+
"- [ ]" in output_content or
|
|
1308
|
+
"- [x]" in output_content)
|
|
1309
|
+
|
|
1310
|
+
if not has_todo_list:
|
|
1280
1311
|
return PhaseResult(
|
|
1281
1312
|
status=PhaseStatus.FAILED,
|
|
1282
|
-
message="Agent did not
|
|
1313
|
+
message="Agent did not write todo list to output.md (missing todo list markers)",
|
|
1283
1314
|
)
|
|
1284
1315
|
|
|
1285
1316
|
# Additional validation: if agent returned CONFIRMED, verify this is correct
|
|
@@ -1332,12 +1363,33 @@ Return ONLY the status code (CAFE_CONFIRMED or CAFE_NEEDS_CHANGES) with no expla
|
|
|
1332
1363
|
pr_dir = self.issue_dir / "pr"
|
|
1333
1364
|
iteration_dir = pr_dir / f"iteration_{self.iteration:03d}"
|
|
1334
1365
|
output_file = iteration_dir / "output.md"
|
|
1366
|
+
# Post todo list as PR comment in GitHub mode (if enabled)
|
|
1367
|
+
if pr_number > 0 and self.post_todo_list: # Only post in GitHub mode when option is enabled
|
|
1368
|
+
try:
|
|
1369
|
+
todo_list_content = output_file.read_text(encoding="utf-8")
|
|
1370
|
+
from cafe.utils.git_utils import to_cwd_relative_path
|
|
1371
|
+
try:
|
|
1372
|
+
user_input_display = to_cwd_relative_path(user_input_file)
|
|
1373
|
+
except ValueError:
|
|
1374
|
+
user_input_display = str(user_input_file)
|
|
1375
|
+
|
|
1376
|
+
comment_body = self._build_todo_list_comment(todo_list_content, user_input_display)
|
|
1377
|
+
self.github_ops.add_pr_comment(str(pr_number), comment_body)
|
|
1378
|
+
except Exception as e:
|
|
1379
|
+
# Don't fail the workflow if posting comment fails - just warn
|
|
1380
|
+
from rich.console import Console
|
|
1381
|
+
console = Console()
|
|
1382
|
+
console.print(f"[yellow]⚠️ Warning: Failed to post todo list as PR comment: {e}[/yellow]")
|
|
1383
|
+
|
|
1335
1384
|
from cafe.utils.git_utils import to_cwd_relative_path
|
|
1336
1385
|
try:
|
|
1337
1386
|
output_display = to_cwd_relative_path(output_file)
|
|
1338
1387
|
except ValueError:
|
|
1339
1388
|
output_display = str(output_file)
|
|
1340
1389
|
|
|
1390
|
+
# Display token usage summary before returning
|
|
1391
|
+
self._print_token_usage_summary()
|
|
1392
|
+
|
|
1341
1393
|
return PhaseResult(
|
|
1342
1394
|
status=PhaseStatus.COMPLETED,
|
|
1343
1395
|
message=f"Organized PR comments into todo list ({output_display})",
|
|
@@ -816,6 +816,11 @@ def prepare(
|
|
|
816
816
|
"--sync-plan-github/--no-sync-plan-github",
|
|
817
817
|
help="Sync plan to GitHub issue when confirmed (default: auto-detect based on issue_id)",
|
|
818
818
|
),
|
|
819
|
+
post_pr_todo_list: Optional[bool] = typer.Option(
|
|
820
|
+
None,
|
|
821
|
+
"--post-pr-todo-list/--no-post-pr-todo-list",
|
|
822
|
+
help="Post organized PR comments as todo list to PR (default: True when auto-create PR is enabled)",
|
|
823
|
+
),
|
|
819
824
|
) -> None:
|
|
820
825
|
"""Prepare issue environment (directory, config, git branch) before running spec phase.
|
|
821
826
|
|
|
@@ -1045,9 +1050,12 @@ def prepare(
|
|
|
1045
1050
|
# Auto create PR depends on whether it's a GitHub repo
|
|
1046
1051
|
if is_github_repo():
|
|
1047
1052
|
pr_config["auto_create"] = True
|
|
1053
|
+
# In Quick setup, auto_create is always True for GitHub repos,
|
|
1054
|
+
# so always enable post_todo_list as well
|
|
1055
|
+
pr_config["post_todo_list"] = True
|
|
1048
1056
|
else:
|
|
1049
1057
|
pr_config["auto_create"] = False
|
|
1050
|
-
|
|
1058
|
+
|
|
1051
1059
|
# Display default values summary
|
|
1052
1060
|
console.print()
|
|
1053
1061
|
console.print("[green]✓ Quick setup applied with recommended defaults:[/green]")
|
|
@@ -1058,6 +1066,7 @@ def prepare(
|
|
|
1058
1066
|
if is_github_repo():
|
|
1059
1067
|
console.print(f" • Sync to GitHub: {spec_config.get('sync_github', False)}")
|
|
1060
1068
|
console.print(f" • Auto create PR: {pr_config.get('auto_create', False)}")
|
|
1069
|
+
console.print(f" • Post PR todo list: {pr_config.get('post_todo_list', False)}")
|
|
1061
1070
|
console.print()
|
|
1062
1071
|
else:
|
|
1063
1072
|
# Custom configuration: Prompt for remaining settings
|
|
@@ -1131,6 +1140,15 @@ def prepare(
|
|
|
1131
1140
|
config_file = Path(".cafe") / "issues" / issue_name / "issue.yaml"
|
|
1132
1141
|
auto_create_pr_result = prompt_and_save_auto_create(config_file, "pr.auto_create")
|
|
1133
1142
|
pr_config["auto_create"] = auto_create_pr_result
|
|
1143
|
+
|
|
1144
|
+
# Prompt for post_todo_list only when auto_create is enabled
|
|
1145
|
+
if auto_create_pr_result:
|
|
1146
|
+
console.print()
|
|
1147
|
+
post_todo_list_result = prompt_confirm(
|
|
1148
|
+
"Post organized PR comments as todo list to PR?",
|
|
1149
|
+
default=True,
|
|
1150
|
+
)
|
|
1151
|
+
pr_config["post_todo_list"] = post_todo_list_result
|
|
1134
1152
|
elif not interactive:
|
|
1135
1153
|
# Explicit non-interactive mode (--no-interactive): use CLI parameters
|
|
1136
1154
|
from cafe.utils.git_utils import is_github_repo
|
|
@@ -1154,6 +1172,8 @@ def prepare(
|
|
|
1154
1172
|
# PR config (only for GitHub repos)
|
|
1155
1173
|
if is_github_repo() and auto_create_pr:
|
|
1156
1174
|
pr_config["auto_create"] = True
|
|
1175
|
+
if post_pr_todo_list is not None:
|
|
1176
|
+
pr_config["post_todo_list"] = post_pr_todo_list
|
|
1157
1177
|
# else: issue_name was provided as argument but not --no-interactive
|
|
1158
1178
|
# Don't save any config (old behavior for backward compatibility)
|
|
1159
1179
|
|
|
@@ -3397,6 +3417,11 @@ def pr(
|
|
|
3397
3417
|
"--interactive/--no-interactive",
|
|
3398
3418
|
help="Allow interactive prompts (default: True)",
|
|
3399
3419
|
),
|
|
3420
|
+
post_todo_list: Optional[bool] = typer.Option(
|
|
3421
|
+
None,
|
|
3422
|
+
"--post-todo-list/--no-post-todo-list",
|
|
3423
|
+
help="Post organized todo list as PR comment (default: auto-detect from config)",
|
|
3424
|
+
),
|
|
3400
3425
|
) -> None:
|
|
3401
3426
|
"""Create pull request for the issue.
|
|
3402
3427
|
|
|
@@ -3470,6 +3495,7 @@ def pr(
|
|
|
3470
3495
|
force_push=force,
|
|
3471
3496
|
interactive=interactive,
|
|
3472
3497
|
base_branch=base if base != "main" else None, # Pass base only if not default
|
|
3498
|
+
post_todo_list=post_todo_list,
|
|
3473
3499
|
)
|
|
3474
3500
|
|
|
3475
3501
|
result = phase.execute()
|
|
@@ -371,6 +371,7 @@ def generate_pr_comments_checklist(
|
|
|
371
371
|
output_file_path: str,
|
|
372
372
|
prev_output_file_path: Optional[str],
|
|
373
373
|
checklist_file_path: Path,
|
|
374
|
+
basic_principles: Optional[str] = None,
|
|
374
375
|
) -> None:
|
|
375
376
|
"""Generate checklist file for PR comments organization.
|
|
376
377
|
|
|
@@ -380,6 +381,7 @@ def generate_pr_comments_checklist(
|
|
|
380
381
|
output_file_path: Path to output.md where todo list will be written
|
|
381
382
|
prev_output_file_path: Path to previous output.md (None if first iteration)
|
|
382
383
|
checklist_file_path: Path where checklist file should be created
|
|
384
|
+
basic_principles: Basic principles text in "- item" format (optional)
|
|
383
385
|
"""
|
|
384
386
|
# Get agent file path
|
|
385
387
|
agent_file = AgentManager.get_agent_file_path(agent_name, "developer")
|
|
@@ -390,8 +392,13 @@ def generate_pr_comments_checklist(
|
|
|
390
392
|
# Get agent guidelines checklist
|
|
391
393
|
agent_guidelines = extract_agent_guidelines_checklist(agent_file)
|
|
392
394
|
|
|
395
|
+
# Convert basic principles to checklist format
|
|
396
|
+
basic_principles_checklist = ""
|
|
397
|
+
if basic_principles:
|
|
398
|
+
basic_principles_checklist = convert_to_checklist(basic_principles, "Basic Principles")
|
|
399
|
+
|
|
393
400
|
# Combine all sections
|
|
394
|
-
checklist_content = f"{execution_steps}\n{agent_guidelines}"
|
|
401
|
+
checklist_content = f"{execution_steps}\n{basic_principles_checklist}\n{agent_guidelines}"
|
|
395
402
|
|
|
396
403
|
# Build placeholders dict
|
|
397
404
|
placeholders = {
|
|
@@ -236,10 +236,10 @@ PR_EXECUTION_STEPS_ITERATION_N = """## Checklist
|
|
|
236
236
|
PR_COMMENTS_ORGANIZATION_STEPS = """## Checklist
|
|
237
237
|
|
|
238
238
|
[ ] Read {agent_file} to understand your role and native language
|
|
239
|
-
[ ] Read {
|
|
239
|
+
[ ] Read {user_input_file} which contains the original PR review comments
|
|
240
240
|
[ ] Check {prev_output_file} (if exists) - skip todo items that are already completed there
|
|
241
241
|
[ ] Group related comments together
|
|
242
242
|
[ ] Convert comments into actionable todo list items (markdown checkbox format: - [ ] Item - keep all items UNCHECKED as they represent work to be done)
|
|
243
|
-
[ ]
|
|
243
|
+
[ ] Write ONLY the organized todo list to {output_file} (do NOT copy original PR comments)
|
|
244
244
|
[ ] Return ONLY the status code in your response
|
|
245
245
|
"""
|
|
@@ -199,10 +199,96 @@ class TestPRCommandGitHubMode:
|
|
|
199
199
|
assert pr_url in result.stdout
|
|
200
200
|
|
|
201
201
|
# Verify subprocess was called with gh pr diff --web
|
|
202
|
-
calls = [call for call in mock_subprocess_run.call_args_list
|
|
202
|
+
calls = [call for call in mock_subprocess_run.call_args_list
|
|
203
203
|
if call[0][0] == ["gh", "pr", "diff", "--web"]]
|
|
204
204
|
assert len(calls) >= 1, "Expected subprocess to be called with gh pr diff --web"
|
|
205
205
|
|
|
206
|
+
|
|
207
|
+
class TestPRCommandPostTodoListOption:
|
|
208
|
+
"""Test --post-todo-list / --no-post-todo-list option passing to PRPhase."""
|
|
209
|
+
|
|
210
|
+
def _make_mocks(self, mock_git_cls, mock_config_cls, mock_agent_cls,
|
|
211
|
+
mock_perm_cls, mock_github_ops_cls, mock_pr_phase_cls):
|
|
212
|
+
"""Helper to set up common mocks."""
|
|
213
|
+
mock_git = MagicMock()
|
|
214
|
+
mock_git.get_current_branch.return_value = "test-issue"
|
|
215
|
+
mock_git_cls.return_value = mock_git
|
|
216
|
+
|
|
217
|
+
mock_config = MagicMock()
|
|
218
|
+
mock_config.get.return_value = {"name": "David", "cli": "copilot"}
|
|
219
|
+
mock_config_cls.return_value = mock_config
|
|
220
|
+
|
|
221
|
+
mock_agent_cls.return_value = MagicMock()
|
|
222
|
+
mock_perm_cls.return_value = MagicMock()
|
|
223
|
+
mock_github_ops_cls.return_value = MagicMock()
|
|
224
|
+
|
|
225
|
+
mock_pr_phase = MagicMock()
|
|
226
|
+
mock_pr_phase.execute.return_value = PhaseResult(
|
|
227
|
+
status=PhaseStatus.COMPLETED,
|
|
228
|
+
message="Done",
|
|
229
|
+
data={"pr_url": "https://github.com/test/repo/pull/1", "pr_number": 1},
|
|
230
|
+
)
|
|
231
|
+
mock_pr_phase_cls.return_value = mock_pr_phase
|
|
232
|
+
return mock_pr_phase_cls
|
|
233
|
+
|
|
234
|
+
@patch("subprocess.run")
|
|
235
|
+
@patch("cafe.ui.cli.GitHubOps")
|
|
236
|
+
@patch("cafe.ui.cli.PermissionHandler")
|
|
237
|
+
@patch("cafe.ui.cli.AgentManager")
|
|
238
|
+
@patch("cafe.ui.cli.ConfigManager")
|
|
239
|
+
@patch("cafe.ui.cli.GitOperations")
|
|
240
|
+
@patch("cafe.ui.cli.PRPhase")
|
|
241
|
+
def test_post_todo_list_flag_passes_true_to_pr_phase(
|
|
242
|
+
self,
|
|
243
|
+
mock_pr_phase_cls,
|
|
244
|
+
mock_git_cls,
|
|
245
|
+
mock_config_cls,
|
|
246
|
+
mock_agent_cls,
|
|
247
|
+
mock_perm_cls,
|
|
248
|
+
mock_github_ops_cls,
|
|
249
|
+
mock_subprocess_run,
|
|
250
|
+
temp_repo_dir,
|
|
251
|
+
):
|
|
252
|
+
"""Test 2.1: --post-todo-list フラグが PRPhase に post_todo_list=True で渡される。"""
|
|
253
|
+
mock_subprocess_run.return_value = MagicMock(returncode=0)
|
|
254
|
+
self._make_mocks(mock_git_cls, mock_config_cls, mock_agent_cls,
|
|
255
|
+
mock_perm_cls, mock_github_ops_cls, mock_pr_phase_cls)
|
|
256
|
+
|
|
257
|
+
result = runner.invoke(app, ["pr", "--post-todo-list"])
|
|
258
|
+
|
|
259
|
+
assert result.exit_code == 0
|
|
260
|
+
call_kwargs = mock_pr_phase_cls.call_args[1]
|
|
261
|
+
assert call_kwargs.get("post_todo_list") is True
|
|
262
|
+
|
|
263
|
+
@patch("subprocess.run")
|
|
264
|
+
@patch("cafe.ui.cli.GitHubOps")
|
|
265
|
+
@patch("cafe.ui.cli.PermissionHandler")
|
|
266
|
+
@patch("cafe.ui.cli.AgentManager")
|
|
267
|
+
@patch("cafe.ui.cli.ConfigManager")
|
|
268
|
+
@patch("cafe.ui.cli.GitOperations")
|
|
269
|
+
@patch("cafe.ui.cli.PRPhase")
|
|
270
|
+
def test_no_post_todo_list_flag_passes_false_to_pr_phase(
|
|
271
|
+
self,
|
|
272
|
+
mock_pr_phase_cls,
|
|
273
|
+
mock_git_cls,
|
|
274
|
+
mock_config_cls,
|
|
275
|
+
mock_agent_cls,
|
|
276
|
+
mock_perm_cls,
|
|
277
|
+
mock_github_ops_cls,
|
|
278
|
+
mock_subprocess_run,
|
|
279
|
+
temp_repo_dir,
|
|
280
|
+
):
|
|
281
|
+
"""Test 2.2: --no-post-todo-list フラグが PRPhase に post_todo_list=False で渡される。"""
|
|
282
|
+
mock_subprocess_run.return_value = MagicMock(returncode=0)
|
|
283
|
+
self._make_mocks(mock_git_cls, mock_config_cls, mock_agent_cls,
|
|
284
|
+
mock_perm_cls, mock_github_ops_cls, mock_pr_phase_cls)
|
|
285
|
+
|
|
286
|
+
result = runner.invoke(app, ["pr", "--no-post-todo-list"])
|
|
287
|
+
|
|
288
|
+
assert result.exit_code == 0
|
|
289
|
+
call_kwargs = mock_pr_phase_cls.call_args[1]
|
|
290
|
+
assert call_kwargs.get("post_todo_list") is False
|
|
291
|
+
|
|
206
292
|
@patch("subprocess.run")
|
|
207
293
|
@patch("cafe.ui.cli.GitHubOps")
|
|
208
294
|
@patch("cafe.ui.cli.PermissionHandler")
|
|
@@ -971,3 +971,189 @@ class TestPrepareCommandSetupMode:
|
|
|
971
971
|
# 驗證沒有詢問 templates 或其他設定
|
|
972
972
|
mock_template_list.assert_not_called()
|
|
973
973
|
mock_phase_confirm.assert_not_called()
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
class TestPrepareCommandPostPrTodoList:
|
|
977
|
+
"""Test --post-pr-todo-list option in cafe prepare command."""
|
|
978
|
+
|
|
979
|
+
@patch("cafe.ui.phase_prompts.prompt_text")
|
|
980
|
+
@patch("cafe.ui.phase_prompts.GitHubOps")
|
|
981
|
+
@patch("cafe.ui.cli.GitHubOps")
|
|
982
|
+
@patch("cafe.ui.phase_prompts.prompt_confirm")
|
|
983
|
+
@patch("cafe.ui.cli.prompt_confirm")
|
|
984
|
+
@patch("cafe.ui.template_selector.prompt_list")
|
|
985
|
+
@patch("cafe.ui.phase_prompts.prompt_list")
|
|
986
|
+
@patch("cafe.ui.cli.prompt_list")
|
|
987
|
+
@patch("cafe.ui.cli.prompt_text")
|
|
988
|
+
def test_quick_setup_sets_post_todo_list_true(
|
|
989
|
+
self,
|
|
990
|
+
mock_prompt_text_cli,
|
|
991
|
+
mock_cli_list,
|
|
992
|
+
mock_phase_list,
|
|
993
|
+
mock_template_list,
|
|
994
|
+
mock_cli_confirm,
|
|
995
|
+
mock_phase_confirm,
|
|
996
|
+
MockGitHubOps_cli,
|
|
997
|
+
MockGitHubOps_phase,
|
|
998
|
+
mock_prompt_text_phase,
|
|
999
|
+
temp_repo_dir,
|
|
1000
|
+
mock_git_ops,
|
|
1001
|
+
):
|
|
1002
|
+
"""Test 3.1: Quick setup モードでは pr_config.post_todo_list が True に設定される。"""
|
|
1003
|
+
mock_github_ops = MagicMock()
|
|
1004
|
+
MockGitHubOps_cli.return_value = mock_github_ops
|
|
1005
|
+
MockGitHubOps_phase.return_value = mock_github_ops
|
|
1006
|
+
mock_github_ops.extract_issue_number.return_value = "123"
|
|
1007
|
+
|
|
1008
|
+
mock_prompt_text_cli.return_value = "quick-setup-test"
|
|
1009
|
+
mock_prompt_text_phase.return_value = "123"
|
|
1010
|
+
mock_cli_confirm.return_value = False # worktree (n)
|
|
1011
|
+
|
|
1012
|
+
mock_phase_list.return_value = "2. Fetch from GitHub Issue"
|
|
1013
|
+
mock_cli_list.return_value = "Quick setup (use recommended defaults)"
|
|
1014
|
+
|
|
1015
|
+
result = runner.invoke(app, ["prepare"])
|
|
1016
|
+
|
|
1017
|
+
assert result.exit_code == 0
|
|
1018
|
+
|
|
1019
|
+
config_file = temp_repo_dir / ".cafe" / "issues" / "quick-setup-test" / "issue.yaml"
|
|
1020
|
+
with open(config_file) as f:
|
|
1021
|
+
config_data = yaml.safe_load(f)
|
|
1022
|
+
assert config_data["pr"]["auto_create"] is True
|
|
1023
|
+
assert config_data["pr"]["post_todo_list"] is True
|
|
1024
|
+
|
|
1025
|
+
@patch("cafe.ui.phase_prompts.prompt_text")
|
|
1026
|
+
@patch("cafe.ui.phase_prompts.GitHubOps")
|
|
1027
|
+
@patch("cafe.ui.cli.GitHubOps")
|
|
1028
|
+
@patch("cafe.ui.phase_prompts.prompt_confirm")
|
|
1029
|
+
@patch("cafe.ui.cli.prompt_confirm")
|
|
1030
|
+
@patch("cafe.ui.template_selector.prompt_list")
|
|
1031
|
+
@patch("cafe.ui.phase_prompts.prompt_list")
|
|
1032
|
+
@patch("cafe.ui.cli.prompt_list")
|
|
1033
|
+
@patch("cafe.ui.cli.prompt_text")
|
|
1034
|
+
def test_custom_config_auto_create_true_prompts_post_todo_list(
|
|
1035
|
+
self,
|
|
1036
|
+
mock_prompt_text_cli,
|
|
1037
|
+
mock_cli_list,
|
|
1038
|
+
mock_phase_list,
|
|
1039
|
+
mock_template_list,
|
|
1040
|
+
mock_cli_confirm,
|
|
1041
|
+
mock_phase_confirm,
|
|
1042
|
+
MockGitHubOps_cli,
|
|
1043
|
+
MockGitHubOps_phase,
|
|
1044
|
+
mock_prompt_text_phase,
|
|
1045
|
+
temp_repo_dir,
|
|
1046
|
+
mock_git_ops,
|
|
1047
|
+
):
|
|
1048
|
+
"""Test 3.2: Custom 設定モードで auto_create=True の場合、post_todo_list が尋ねられる。"""
|
|
1049
|
+
mock_github_ops = MagicMock()
|
|
1050
|
+
MockGitHubOps_cli.return_value = mock_github_ops
|
|
1051
|
+
MockGitHubOps_phase.return_value = mock_github_ops
|
|
1052
|
+
mock_github_ops.extract_issue_number.return_value = "42"
|
|
1053
|
+
|
|
1054
|
+
mock_prompt_text_cli.return_value = "custom-auto-create"
|
|
1055
|
+
mock_prompt_text_phase.return_value = "42"
|
|
1056
|
+
# worktree=No, sync_spec=True, sync_plan=True, auto_create=True, post_todo_list=False
|
|
1057
|
+
mock_cli_confirm.side_effect = [False, True, True, True, False]
|
|
1058
|
+
mock_phase_confirm.side_effect = [True] # auto_create prompt in phase_prompts
|
|
1059
|
+
|
|
1060
|
+
mock_phase_list.return_value = "2. Fetch from GitHub Issue"
|
|
1061
|
+
mock_cli_list.return_value = "Custom configuration"
|
|
1062
|
+
mock_template_list.return_value = "default (system default)"
|
|
1063
|
+
|
|
1064
|
+
result = runner.invoke(app, ["prepare"])
|
|
1065
|
+
|
|
1066
|
+
assert result.exit_code == 0
|
|
1067
|
+
|
|
1068
|
+
# post_todo_list should have been set (either True or False from prompt)
|
|
1069
|
+
config_file = temp_repo_dir / ".cafe" / "issues" / "custom-auto-create" / "issue.yaml"
|
|
1070
|
+
with open(config_file) as f:
|
|
1071
|
+
config_data = yaml.safe_load(f)
|
|
1072
|
+
# post_todo_list should be present when auto_create is True
|
|
1073
|
+
assert "post_todo_list" in config_data.get("pr", {})
|
|
1074
|
+
|
|
1075
|
+
@patch("cafe.ui.phase_prompts.prompt_text")
|
|
1076
|
+
@patch("cafe.ui.phase_prompts.GitHubOps")
|
|
1077
|
+
@patch("cafe.ui.cli.GitHubOps")
|
|
1078
|
+
@patch("cafe.ui.phase_prompts.prompt_confirm")
|
|
1079
|
+
@patch("cafe.ui.cli.prompt_confirm")
|
|
1080
|
+
@patch("cafe.ui.template_selector.prompt_list")
|
|
1081
|
+
@patch("cafe.ui.phase_prompts.prompt_list")
|
|
1082
|
+
@patch("cafe.ui.cli.prompt_list")
|
|
1083
|
+
@patch("cafe.ui.cli.prompt_text")
|
|
1084
|
+
def test_custom_config_auto_create_false_does_not_prompt_post_todo_list(
|
|
1085
|
+
self,
|
|
1086
|
+
mock_prompt_text_cli,
|
|
1087
|
+
mock_cli_list,
|
|
1088
|
+
mock_phase_list,
|
|
1089
|
+
mock_template_list,
|
|
1090
|
+
mock_cli_confirm,
|
|
1091
|
+
mock_phase_confirm,
|
|
1092
|
+
MockGitHubOps_cli,
|
|
1093
|
+
MockGitHubOps_phase,
|
|
1094
|
+
mock_prompt_text_phase,
|
|
1095
|
+
temp_repo_dir,
|
|
1096
|
+
mock_git_ops,
|
|
1097
|
+
):
|
|
1098
|
+
"""Test 3.3: Custom 設定モードで auto_create=False の場合、post_todo_list は尋ねられない。"""
|
|
1099
|
+
mock_github_ops = MagicMock()
|
|
1100
|
+
MockGitHubOps_cli.return_value = mock_github_ops
|
|
1101
|
+
MockGitHubOps_phase.return_value = mock_github_ops
|
|
1102
|
+
mock_github_ops.extract_issue_number.return_value = "42"
|
|
1103
|
+
|
|
1104
|
+
mock_prompt_text_cli.return_value = "custom-no-auto-create"
|
|
1105
|
+
mock_prompt_text_phase.return_value = "42"
|
|
1106
|
+
# worktree=No, sync_spec=True, sync_plan=True
|
|
1107
|
+
mock_cli_confirm.side_effect = [False, True, True]
|
|
1108
|
+
# auto_create prompt in phase_prompts returns False
|
|
1109
|
+
mock_phase_confirm.side_effect = [False]
|
|
1110
|
+
|
|
1111
|
+
mock_phase_list.return_value = "2. Fetch from GitHub Issue"
|
|
1112
|
+
mock_cli_list.return_value = "Custom configuration"
|
|
1113
|
+
mock_template_list.return_value = "default (system default)"
|
|
1114
|
+
|
|
1115
|
+
result = runner.invoke(app, ["prepare"])
|
|
1116
|
+
|
|
1117
|
+
assert result.exit_code == 0
|
|
1118
|
+
|
|
1119
|
+
# post_todo_list should NOT be set when auto_create is False
|
|
1120
|
+
config_file = temp_repo_dir / ".cafe" / "issues" / "custom-no-auto-create" / "issue.yaml"
|
|
1121
|
+
with open(config_file) as f:
|
|
1122
|
+
config_data = yaml.safe_load(f)
|
|
1123
|
+
assert "post_todo_list" not in config_data.get("pr", {})
|
|
1124
|
+
|
|
1125
|
+
def test_non_interactive_post_pr_todo_list_flag_saves_to_config(self, temp_repo_dir, mock_git_ops):
|
|
1126
|
+
"""Test 3.4/3.5: --no-interactive モードで --post-pr-todo-list フラグが issue.yaml に保存される。"""
|
|
1127
|
+
result = runner.invoke(app, [
|
|
1128
|
+
"prepare", "noninteractive-test",
|
|
1129
|
+
"--no-interactive",
|
|
1130
|
+
"--input-method", "manual",
|
|
1131
|
+
"--rigor", "medium",
|
|
1132
|
+
"--auto-create-pr",
|
|
1133
|
+
"--post-pr-todo-list",
|
|
1134
|
+
])
|
|
1135
|
+
|
|
1136
|
+
assert result.exit_code == 0
|
|
1137
|
+
|
|
1138
|
+
config_file = temp_repo_dir / ".cafe" / "issues" / "noninteractive-test" / "issue.yaml"
|
|
1139
|
+
with open(config_file) as f:
|
|
1140
|
+
config_data = yaml.safe_load(f)
|
|
1141
|
+
assert config_data["pr"]["post_todo_list"] is True
|
|
1142
|
+
|
|
1143
|
+
def test_non_interactive_no_post_pr_todo_list_flag_saves_false_to_config(self, temp_repo_dir, mock_git_ops):
|
|
1144
|
+
"""Test 3.4b: --no-interactive モードで --no-post-pr-todo-list フラグが issue.yaml に保存される。"""
|
|
1145
|
+
result = runner.invoke(app, [
|
|
1146
|
+
"prepare", "noninteractive-false-test",
|
|
1147
|
+
"--no-interactive",
|
|
1148
|
+
"--input-method", "manual",
|
|
1149
|
+
"--rigor", "medium",
|
|
1150
|
+
"--auto-create-pr",
|
|
1151
|
+
"--no-post-pr-todo-list",
|
|
1152
|
+
])
|
|
1153
|
+
|
|
1154
|
+
assert result.exit_code == 0
|
|
1155
|
+
|
|
1156
|
+
config_file = temp_repo_dir / ".cafe" / "issues" / "noninteractive-false-test" / "issue.yaml"
|
|
1157
|
+
with open(config_file) as f:
|
|
1158
|
+
config_data = yaml.safe_load(f)
|
|
1159
|
+
assert config_data["pr"]["post_todo_list"] is False
|