minitest-cli 0.35.0__tar.gz → 0.37.0__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.
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.env.example +6 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/AGENTS.md +1 -1
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/PKG-INFO +5 -26
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/README.md +4 -25
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/pyproject.toml +1 -1
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/client.py +10 -4
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill_helpers.py +10 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill_write.py +38 -2
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps.py +4 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build.py +2 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build_fix_prompts.py +15 -10
- minitest_cli-0.37.0/src/minitest_cli/commands/build_get.py +50 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft.py +111 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_asks.py +148 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_conversation.py +84 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_create.py +66 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature.py +9 -6
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_apply.py +1 -1
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_conflicts.py +1 -1
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_helpers.py +1 -3
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_show.py +1 -1
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_helpers.py +131 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_legacy.py +40 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_lifecycle.py +103 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_pr.py +49 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_render.py +110 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_update.py +45 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/draft_watch.py +90 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/env_helpers.py +4 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues.py +2 -0
- minitest_cli-0.37.0/src/minitest_cli/commands/issues_get.py +33 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile.py +5 -1
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_helpers.py +9 -1
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_list.py +3 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_update.py +2 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/config.py +5 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/main.py +2 -3
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/build.py +4 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/issues.py +5 -0
- minitest_cli-0.37.0/tests/draft_backend.py +98 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_app_skill_commands.py +50 -0
- minitest_cli-0.37.0/tests/test_build_get_command.py +106 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_deprecated_commands.py +18 -3
- minitest_cli-0.37.0/tests/test_draft_ask_commands.py +120 -0
- minitest_cli-0.37.0/tests/test_draft_commands.py +167 -0
- minitest_cli-0.37.0/tests/test_draft_conversation_commands.py +133 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_draft_feature_commands.py +1 -1
- minitest_cli-0.37.0/tests/test_draft_pr_commands.py +135 -0
- minitest_cli-0.37.0/tests/test_issues_get_command.py +90 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_test_profile_commands.py +25 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/uv.lock +1 -1
- minitest_cli-0.35.0/src/minitest_cli/commands/maintenance.py +0 -130
- minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_callbacks.py +0 -86
- minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_helpers.py +0 -132
- minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_state.py +0 -56
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.github/workflows/ci.yml +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.github/workflows/install-scripts.yml +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.github/workflows/release.yml +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.gitignore +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.opencode/skill/release/SKILL.md +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/RELEASE.md +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/install.ps1 +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/install.sh +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/pyrightconfig.json +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/renovate.json +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/apps_manager_client.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/errors.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/assets/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/assets/callback.html +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/_response_errors.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_knowledge.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_knowledge_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_network_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill_secret.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_dependencies.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_get.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_table.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/auth.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/auth_api_key.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/batch.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/batch_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/batch_warnings.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build_commit.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/capabilities.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/commit_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/deprecated_alias.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/env.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/flow_types.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/flow_types_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/flow_types_write.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/init.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/init_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/init_playbook.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_fix.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_projection.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_service.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/private_network.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/private_network_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/recording_timeline.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/residential_proxy.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_cancel.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_commit.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_commit_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_display.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_feedback.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_recording.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_recording_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_tagged.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_targets.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_detail.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_format.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_tree.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/skill.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/tags.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/tags_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_file.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_file_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_file_list.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_card.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_default.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/upgrade.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_app_skills.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_bindings.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_camera.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_create.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_criteria.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_criterion_edits.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_delete.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_device_count.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_modify.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_overrides.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_profiles.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_setup_commit.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_update.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/verdicts.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/verdicts_helpers.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/verdicts_projection.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/app_context.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/auth.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/credentials.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/oauth.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/tenants.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/token_exchange.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app_env_vars.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app_failure.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app_screen.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/base.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/batch.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/commit_build.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/draft_feature.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/flow_type.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/recording.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/story_run.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/tag.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/targets.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/user_story.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/confirm.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/dependency_sim.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/mermaid.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/output.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/skill_refresh.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/update_check.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/video_frames.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/__init__.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/_commit_transport.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/_network_settings_server.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/conftest.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_api_client_auth.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_app_knowledge_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_apps_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_apps_dependencies.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_auth.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_auth_api_key.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_auth_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_batch_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_build_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_build_fix_prompts.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_capabilities_command.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_channel_header.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_code_quality.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_commit_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_deprecated_flags.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_env.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_flow_types_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_init_command.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_issues.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_issues_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_mermaid.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_native_device_types.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_network_errors.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_private_network_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_recording_timeline.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_residential_proxy_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_response_errors.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_run_cancel.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_run_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_run_frames_command.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_screens_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_skill_command.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_suite_change_provenance.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_tags_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_test_file_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_upgrade_command.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_user_story_bindings_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_user_story_commands.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_user_story_setup_commit.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_verdicts.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_verdicts_actionable.py +0 -0
- {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_version.py +0 -0
|
@@ -16,6 +16,12 @@ MINITEST_API_URL=https://testing-service.dev.minitap.ai
|
|
|
16
16
|
# Development
|
|
17
17
|
MINITEST_APPS_MANAGER_URL=https://apps-manager.dev.minitap.ai
|
|
18
18
|
|
|
19
|
+
# —— Conductor (minitest draft) ——
|
|
20
|
+
# Production
|
|
21
|
+
# MINITEST_CONDUCTOR_URL=https://conductor.app.minitap.ai
|
|
22
|
+
# Development
|
|
23
|
+
MINITEST_CONDUCTOR_URL=https://conductor.dev.minitap.ai
|
|
24
|
+
|
|
19
25
|
# —— Integrations ——
|
|
20
26
|
# Production
|
|
21
27
|
# MINITEST_INTEGRATIONS_URL=https://integrations.minitap.ai
|
|
@@ -14,7 +14,7 @@ uv add <package> # Add new dependency (always
|
|
|
14
14
|
## Project Structure
|
|
15
15
|
|
|
16
16
|
- `src/minitest_cli/` - Main package
|
|
17
|
-
- `commands/` - One Typer sub-app per command group (auth, apps, flow, build, run
|
|
17
|
+
- `commands/` - One Typer sub-app per command group (auth, apps, flow, build, run)
|
|
18
18
|
- `core/`
|
|
19
19
|
- `config.py` - pydantic-settings: MINITEST_API_URL, MINITEST_TOKEN, config dir
|
|
20
20
|
- `app_context.py` - --app flag / MINITEST_APP_ID resolution
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: minitest-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37.0
|
|
4
4
|
Summary: Minitest CLI – command-line interface for the Minitest testing platform
|
|
5
5
|
Project-URL: Homepage, https://minitap.ai/
|
|
6
6
|
Project-URL: Source, https://github.com/minitap-ai/minitest-cli
|
|
@@ -145,6 +145,7 @@ app defaults.
|
|
|
145
145
|
| `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
|
|
146
146
|
| `MINITEST_API_URL` | testing-service base URL | No (defaults to production) |
|
|
147
147
|
| `MINITEST_APPS_MANAGER_URL` | apps-manager base URL (used by `minitest apps create`) | No (defaults to production) |
|
|
148
|
+
| `MINITEST_CONDUCTOR_URL` | Conductor base URL (used by `minitest draft`) | No (defaults to production) |
|
|
148
149
|
| `MINITEST_INTEGRATIONS_URL` | minihands-integrations base URL (used to list tenants) | No (defaults to production) |
|
|
149
150
|
| `MINITEST_WEBAPP_URL` | Minitest webapp base URL (used for review links) | No (defaults to production) |
|
|
150
151
|
| `MINITEST_SUPABASE_URL` | Supabase project URL used for OAuth login | No (defaults to production) |
|
|
@@ -169,7 +170,8 @@ app defaults.
|
|
|
169
170
|
| `minitest apps` | List, inspect, and create apps; residential proxy and private network settings |
|
|
170
171
|
| `minitest scenario` | Create, update, list and delete scenarios |
|
|
171
172
|
| `minitest scenario-binding` | Bind test accounts, test files or app skills to scenarios |
|
|
172
|
-
| `minitest draft` |
|
|
173
|
+
| `minitest draft` | Ask Mini for a suite change, follow it, check and merge it (drafts in the webapp) |
|
|
174
|
+
| `minitest df` | Low-level suite branches Mini writes (draft features) |
|
|
173
175
|
| `minitest test-profile` | Test profiles (Test accounts in the webapp) |
|
|
174
176
|
| `minitest tags` | List, create, update and delete scenario tags |
|
|
175
177
|
| `minitest app-skill` | App skills (Skills in the webapp): procedures Mini loads to arrange test state |
|
|
@@ -178,7 +180,6 @@ app defaults.
|
|
|
178
180
|
| `minitest run` | Start runs and inspect their scenario runs |
|
|
179
181
|
| `minitest batch` | List, inspect and cancel runs (a batch is a Run in the webapp) |
|
|
180
182
|
| `minitest issues` | Read app issues and their fix prompts, mark them fixed |
|
|
181
|
-
| `minitest maintenance` | CLI-only scenario maintenance against local code |
|
|
182
183
|
| `minitest skill` | Print the CLI skill for your AI coding agent (not app skills) |
|
|
183
184
|
|
|
184
185
|
### Deprecated names
|
|
@@ -191,7 +192,7 @@ stderr and leave `--json` stdout unchanged. Switch to the new names.
|
|
|
191
192
|
| `minitest user-story` | `minitest scenario` |
|
|
192
193
|
| `minitest user-story-binding` | `minitest scenario-binding` |
|
|
193
194
|
| `minitest scenario-binding set-skills` | `minitest scenario-binding set-app-skills` |
|
|
194
|
-
| `minitest
|
|
195
|
+
| `minitest draft show/apply/delete` (v0.34–v0.36) | `minitest df show/apply/delete` |
|
|
195
196
|
| `minitest flow-types` | `minitest tags` |
|
|
196
197
|
| `run from-commit --user-story` / `-u` | `run from-commit --scenario` / `-s` |
|
|
197
198
|
| `batch list --user-story-id` | `batch list --scenario` |
|
|
@@ -226,28 +227,6 @@ least one domain. Connectors cannot be created from the CLI: a workspace owner
|
|
|
226
227
|
or admin creates them in the webapp, under **Workspace settings → Private
|
|
227
228
|
network**.
|
|
228
229
|
|
|
229
|
-
## CLI-only maintenance
|
|
230
|
-
|
|
231
|
-
`minitest maintenance` lets a coding agent keep Minitest scenarios in sync
|
|
232
|
-
without connecting GitHub. Run it from the app repository: the code stays on the
|
|
233
|
-
machine, while the CLI sends only proposed scenario edits and the local HEAD SHA.
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
# Print the server-composed maintenance instructions for your coding agent
|
|
237
|
-
minitest maintenance --agent
|
|
238
|
-
|
|
239
|
-
# Agent workflow primitives
|
|
240
|
-
minitest --json maintenance context
|
|
241
|
-
minitest maintenance affected --file affected.json
|
|
242
|
-
minitest maintenance change --file change.json
|
|
243
|
-
minitest maintenance status --phase writing --message "Updating affected scenarios"
|
|
244
|
-
minitest maintenance complete --changed
|
|
245
|
-
|
|
246
|
-
# Apply proposed edits now, or open the web review queue
|
|
247
|
-
minitest maintenance apply
|
|
248
|
-
minitest maintenance apply --review
|
|
249
|
-
```
|
|
250
|
-
|
|
251
230
|
## Exit Codes
|
|
252
231
|
|
|
253
232
|
| Code | Meaning |
|
|
@@ -112,6 +112,7 @@ app defaults.
|
|
|
112
112
|
| `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
|
|
113
113
|
| `MINITEST_API_URL` | testing-service base URL | No (defaults to production) |
|
|
114
114
|
| `MINITEST_APPS_MANAGER_URL` | apps-manager base URL (used by `minitest apps create`) | No (defaults to production) |
|
|
115
|
+
| `MINITEST_CONDUCTOR_URL` | Conductor base URL (used by `minitest draft`) | No (defaults to production) |
|
|
115
116
|
| `MINITEST_INTEGRATIONS_URL` | minihands-integrations base URL (used to list tenants) | No (defaults to production) |
|
|
116
117
|
| `MINITEST_WEBAPP_URL` | Minitest webapp base URL (used for review links) | No (defaults to production) |
|
|
117
118
|
| `MINITEST_SUPABASE_URL` | Supabase project URL used for OAuth login | No (defaults to production) |
|
|
@@ -136,7 +137,8 @@ app defaults.
|
|
|
136
137
|
| `minitest apps` | List, inspect, and create apps; residential proxy and private network settings |
|
|
137
138
|
| `minitest scenario` | Create, update, list and delete scenarios |
|
|
138
139
|
| `minitest scenario-binding` | Bind test accounts, test files or app skills to scenarios |
|
|
139
|
-
| `minitest draft` |
|
|
140
|
+
| `minitest draft` | Ask Mini for a suite change, follow it, check and merge it (drafts in the webapp) |
|
|
141
|
+
| `minitest df` | Low-level suite branches Mini writes (draft features) |
|
|
140
142
|
| `minitest test-profile` | Test profiles (Test accounts in the webapp) |
|
|
141
143
|
| `minitest tags` | List, create, update and delete scenario tags |
|
|
142
144
|
| `minitest app-skill` | App skills (Skills in the webapp): procedures Mini loads to arrange test state |
|
|
@@ -145,7 +147,6 @@ app defaults.
|
|
|
145
147
|
| `minitest run` | Start runs and inspect their scenario runs |
|
|
146
148
|
| `minitest batch` | List, inspect and cancel runs (a batch is a Run in the webapp) |
|
|
147
149
|
| `minitest issues` | Read app issues and their fix prompts, mark them fixed |
|
|
148
|
-
| `minitest maintenance` | CLI-only scenario maintenance against local code |
|
|
149
150
|
| `minitest skill` | Print the CLI skill for your AI coding agent (not app skills) |
|
|
150
151
|
|
|
151
152
|
### Deprecated names
|
|
@@ -158,7 +159,7 @@ stderr and leave `--json` stdout unchanged. Switch to the new names.
|
|
|
158
159
|
| `minitest user-story` | `minitest scenario` |
|
|
159
160
|
| `minitest user-story-binding` | `minitest scenario-binding` |
|
|
160
161
|
| `minitest scenario-binding set-skills` | `minitest scenario-binding set-app-skills` |
|
|
161
|
-
| `minitest
|
|
162
|
+
| `minitest draft show/apply/delete` (v0.34–v0.36) | `minitest df show/apply/delete` |
|
|
162
163
|
| `minitest flow-types` | `minitest tags` |
|
|
163
164
|
| `run from-commit --user-story` / `-u` | `run from-commit --scenario` / `-s` |
|
|
164
165
|
| `batch list --user-story-id` | `batch list --scenario` |
|
|
@@ -193,28 +194,6 @@ least one domain. Connectors cannot be created from the CLI: a workspace owner
|
|
|
193
194
|
or admin creates them in the webapp, under **Workspace settings → Private
|
|
194
195
|
network**.
|
|
195
196
|
|
|
196
|
-
## CLI-only maintenance
|
|
197
|
-
|
|
198
|
-
`minitest maintenance` lets a coding agent keep Minitest scenarios in sync
|
|
199
|
-
without connecting GitHub. Run it from the app repository: the code stays on the
|
|
200
|
-
machine, while the CLI sends only proposed scenario edits and the local HEAD SHA.
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
# Print the server-composed maintenance instructions for your coding agent
|
|
204
|
-
minitest maintenance --agent
|
|
205
|
-
|
|
206
|
-
# Agent workflow primitives
|
|
207
|
-
minitest --json maintenance context
|
|
208
|
-
minitest maintenance affected --file affected.json
|
|
209
|
-
minitest maintenance change --file change.json
|
|
210
|
-
minitest maintenance status --phase writing --message "Updating affected scenarios"
|
|
211
|
-
minitest maintenance complete --changed
|
|
212
|
-
|
|
213
|
-
# Apply proposed edits now, or open the web review queue
|
|
214
|
-
minitest maintenance apply
|
|
215
|
-
minitest maintenance apply --review
|
|
216
|
-
```
|
|
217
|
-
|
|
218
197
|
## Exit Codes
|
|
219
198
|
|
|
220
199
|
| Code | Meaning |
|
|
@@ -13,8 +13,7 @@ CONVERSATION_ID_HEADER = "X-Minitest-Conversation-Id"
|
|
|
13
13
|
CAUSE_HEADER = "X-Minitest-Cause"
|
|
14
14
|
DEFAULT_TIMEOUT = 30.0
|
|
15
15
|
UPLOAD_TIMEOUT = 300.0 # 5 minutes for large file uploads
|
|
16
|
-
# Creating a batch
|
|
17
|
-
# POST is not idempotent — timing out client-side leaves a batch running unseen.
|
|
16
|
+
# Creating a batch is not idempotent — timing out client-side leaves a batch running unseen.
|
|
18
17
|
BATCH_CREATE_TIMEOUT = 180.0
|
|
19
18
|
|
|
20
19
|
|
|
@@ -40,9 +39,16 @@ class ApiClient:
|
|
|
40
39
|
response = await client.get("/api/v1/apps")
|
|
41
40
|
"""
|
|
42
41
|
|
|
43
|
-
def __init__(
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
settings: Settings,
|
|
45
|
+
*,
|
|
46
|
+
token_override: str | None = None,
|
|
47
|
+
base_url: str | None = None,
|
|
48
|
+
) -> None:
|
|
44
49
|
self._settings = settings
|
|
45
50
|
self._token_override = token_override
|
|
51
|
+
self._base_url = base_url or settings.api_url
|
|
46
52
|
self._client: httpx.AsyncClient | None = None
|
|
47
53
|
|
|
48
54
|
async def __aenter__(self) -> "ApiClient":
|
|
@@ -54,7 +60,7 @@ class ApiClient:
|
|
|
54
60
|
if self._settings.cause is not None:
|
|
55
61
|
headers[CAUSE_HEADER] = self._settings.cause
|
|
56
62
|
self._client = httpx.AsyncClient(
|
|
57
|
-
base_url=self.
|
|
63
|
+
base_url=self._base_url,
|
|
58
64
|
headers=headers,
|
|
59
65
|
auth=_BearerAuth(self._settings, self._token_override),
|
|
60
66
|
timeout=DEFAULT_TIMEOUT,
|
|
@@ -103,6 +103,16 @@ def read_instructions(instructions: str | None, instructions_file: Path | None)
|
|
|
103
103
|
return instructions
|
|
104
104
|
|
|
105
105
|
|
|
106
|
+
def instruction_edits(
|
|
107
|
+
replace: list[str] | None, replacements: list[str] | None
|
|
108
|
+
) -> list[dict[str, str]]:
|
|
109
|
+
old, new = replace or [], replacements or []
|
|
110
|
+
if len(old) != len(new):
|
|
111
|
+
print_error("Give exactly one --with for each --replace.")
|
|
112
|
+
raise typer.Exit(code=1)
|
|
113
|
+
return [{"oldText": o, "newText": n} for o, n in zip(old, new, strict=True)]
|
|
114
|
+
|
|
115
|
+
|
|
106
116
|
def warn_undefined_secrets(skill: dict[str, Any]) -> None:
|
|
107
117
|
undefined = skill.get("undefinedSecrets") or []
|
|
108
118
|
if undefined:
|
|
@@ -8,11 +8,12 @@ import typer
|
|
|
8
8
|
from minitest_cli.commands.app_skill_helpers import (
|
|
9
9
|
call,
|
|
10
10
|
command_context,
|
|
11
|
+
instruction_edits,
|
|
11
12
|
read_instructions,
|
|
12
13
|
skills_path,
|
|
13
14
|
warn_undefined_secrets,
|
|
14
15
|
)
|
|
15
|
-
from minitest_cli.utils.output import print_error, print_json, print_success
|
|
16
|
+
from minitest_cli.utils.output import print_error, print_info, print_json, print_success
|
|
16
17
|
|
|
17
18
|
Description = Annotated[
|
|
18
19
|
str | None,
|
|
@@ -31,6 +32,24 @@ InstructionsFile = Annotated[
|
|
|
31
32
|
"--instructions-file", exists=True, readable=True, dir_okay=False, help="Markdown file."
|
|
32
33
|
),
|
|
33
34
|
]
|
|
35
|
+
Replace = Annotated[
|
|
36
|
+
list[str] | None,
|
|
37
|
+
typer.Option(
|
|
38
|
+
"--replace",
|
|
39
|
+
help="Exact text of the instructions to replace; pair each with a --with (repeatable).",
|
|
40
|
+
),
|
|
41
|
+
]
|
|
42
|
+
With = Annotated[
|
|
43
|
+
list[str] | None,
|
|
44
|
+
typer.Option("--with", help="Replacement for the matching --replace; empty deletes it."),
|
|
45
|
+
]
|
|
46
|
+
AllowDestructive = Annotated[
|
|
47
|
+
bool,
|
|
48
|
+
typer.Option(
|
|
49
|
+
"--allow-destructive",
|
|
50
|
+
help="Accept a version that removes runnable code or most of the instructions.",
|
|
51
|
+
),
|
|
52
|
+
]
|
|
34
53
|
AllPlatforms = Annotated[
|
|
35
54
|
bool, typer.Option("--all-platforms", help="Make the skill apply to every platform.")
|
|
36
55
|
]
|
|
@@ -94,17 +113,34 @@ def register(app: typer.Typer) -> None:
|
|
|
94
113
|
all_platforms: AllPlatforms = False,
|
|
95
114
|
instructions: Instructions = None,
|
|
96
115
|
instructions_file: InstructionsFile = None,
|
|
116
|
+
replace: Replace = None,
|
|
117
|
+
with_: With = None,
|
|
118
|
+
allow_destructive: AllowDestructive = False,
|
|
97
119
|
) -> None:
|
|
98
|
-
"""Change a skill; every change becomes a new version.
|
|
120
|
+
"""Change a skill; every change becomes a new version.
|
|
121
|
+
|
|
122
|
+
Change part of the instructions with --replace/--with pairs instead of resending them.
|
|
123
|
+
"""
|
|
99
124
|
settings, json_mode, app_id = command_context()
|
|
100
125
|
body = _patch(description, platforms, all_platforms, instructions, instructions_file)
|
|
126
|
+
edits = instruction_edits(replace, with_)
|
|
127
|
+
if edits and "instructions" in body:
|
|
128
|
+
print_error("Use either --replace/--with or --instructions, not both.")
|
|
129
|
+
raise typer.Exit(code=1)
|
|
130
|
+
if edits:
|
|
131
|
+
body["edits"] = edits
|
|
101
132
|
if new_name is not None:
|
|
102
133
|
body["name"] = new_name
|
|
103
134
|
if not body:
|
|
104
135
|
print_error("Nothing to update.")
|
|
105
136
|
raise typer.Exit(code=1)
|
|
137
|
+
if allow_destructive:
|
|
138
|
+
body["allowDestructive"] = True
|
|
106
139
|
skill = call(settings, "PATCH", f"{skills_path(app_id)}/{name}", json=body)
|
|
107
140
|
_report(skill, json_mode, f"Skill {skill['name']} is now v{skill['version']}.")
|
|
141
|
+
if not json_mode:
|
|
142
|
+
for change in skill.get("changes", []):
|
|
143
|
+
print_info(f" {change}")
|
|
108
144
|
|
|
109
145
|
@app.command(name="propose")
|
|
110
146
|
def propose_update(
|
|
@@ -30,6 +30,7 @@ from minitest_cli.utils.output import (
|
|
|
30
30
|
print_table,
|
|
31
31
|
)
|
|
32
32
|
|
|
33
|
+
EXIT_GENERAL_ERROR = 1
|
|
33
34
|
EXIT_NETWORK_ERROR = 3
|
|
34
35
|
|
|
35
36
|
app = typer.Typer(name="apps", help="App management.")
|
|
@@ -70,6 +71,9 @@ def list_apps() -> None:
|
|
|
70
71
|
detail = body.get("detail") or body.get("message") or detail
|
|
71
72
|
except Exception: # noqa: BLE001
|
|
72
73
|
pass
|
|
74
|
+
if resp.status_code in (401, 403):
|
|
75
|
+
print_error(f"Authentication failed ({resp.status_code}): {detail}")
|
|
76
|
+
raise typer.Exit(code=EXIT_GENERAL_ERROR)
|
|
73
77
|
print_error(f"API error ({resp.status_code}): {detail}")
|
|
74
78
|
raise typer.Exit(code=EXIT_NETWORK_ERROR)
|
|
75
79
|
return AppListResponse.model_validate(resp.json())
|
|
@@ -22,6 +22,7 @@ from minitest_cli.commands.build_helpers import (
|
|
|
22
22
|
upload_status_message,
|
|
23
23
|
)
|
|
24
24
|
from minitest_cli.commands.build_commit import from_commit
|
|
25
|
+
from minitest_cli.commands.build_get import get
|
|
25
26
|
from minitest_cli.commands.build_fix_prompts import print_fix_prompts, redacted_payload
|
|
26
27
|
from minitest_cli.models import BuildListResponse, BuildResponse
|
|
27
28
|
from minitest_cli.utils.output import (
|
|
@@ -145,4 +146,5 @@ def list_builds(
|
|
|
145
146
|
print_fix_prompts(result.items)
|
|
146
147
|
|
|
147
148
|
|
|
149
|
+
app.command(name="get")(get)
|
|
148
150
|
app.command(name="from-commit")(from_commit)
|
|
@@ -40,18 +40,23 @@ def build_guidance(build: BuildResponse) -> dict[str, str | None]:
|
|
|
40
40
|
return {"source": "none", "text": None}
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def redacted_build(build: BuildResponse) -> dict[str, Any]:
|
|
44
|
+
item = build.model_dump(mode="json", by_alias=True)
|
|
45
|
+
if build.status != FAILED_STATUS:
|
|
46
|
+
item["guidance"] = None
|
|
47
|
+
return item
|
|
48
|
+
item["guidance"] = build_guidance(build)
|
|
49
|
+
if is_withheld(build.error_class):
|
|
50
|
+
item["errorSummary"] = WITHHELD_NOTICE
|
|
51
|
+
item["errorRemediation"] = None
|
|
52
|
+
item["errorFixPrompt"] = None
|
|
53
|
+
item["errorRaw"] = None
|
|
54
|
+
return item
|
|
55
|
+
|
|
56
|
+
|
|
43
57
|
def redacted_payload(result: BuildListResponse) -> dict[str, Any]:
|
|
44
58
|
payload = result.model_dump(mode="json", by_alias=True)
|
|
45
|
-
|
|
46
|
-
if build.status != FAILED_STATUS:
|
|
47
|
-
item["guidance"] = None
|
|
48
|
-
continue
|
|
49
|
-
item["guidance"] = build_guidance(build)
|
|
50
|
-
if is_withheld(build.error_class):
|
|
51
|
-
item["errorSummary"] = WITHHELD_NOTICE
|
|
52
|
-
item["errorRemediation"] = None
|
|
53
|
-
item["errorFixPrompt"] = None
|
|
54
|
-
item["errorRaw"] = None
|
|
59
|
+
payload["items"] = [redacted_build(build) for build in result.items]
|
|
55
60
|
return payload
|
|
56
61
|
|
|
57
62
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""``minitest build get``: read one build of the active app by id."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from minitest_cli.api.client import ApiClient
|
|
8
|
+
from minitest_cli.commands.build_fix_prompts import print_fix_prompts, redacted_build
|
|
9
|
+
from minitest_cli.commands.build_helpers import (
|
|
10
|
+
BUILD_TABLE_HEADERS,
|
|
11
|
+
base_path,
|
|
12
|
+
format_build_row,
|
|
13
|
+
handle_response_error,
|
|
14
|
+
resolve_app,
|
|
15
|
+
run_api_call,
|
|
16
|
+
)
|
|
17
|
+
from minitest_cli.commands.run_helpers import ensure_uuid
|
|
18
|
+
from minitest_cli.models import BuildResponse
|
|
19
|
+
from minitest_cli.utils.output import output, print_table
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _version_label(build: BuildResponse) -> str | None:
|
|
23
|
+
if build.app_version and build.build_number:
|
|
24
|
+
return f"{build.app_version} ({build.build_number})"
|
|
25
|
+
return build.app_version or build.build_number
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get(
|
|
29
|
+
build_id: Annotated[str, typer.Argument(help="Build ID (from `minitest build list`).")],
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Show one build of the active app: status, version, commit and failure guidance."""
|
|
32
|
+
ensure_uuid(build_id, kind="build ID")
|
|
33
|
+
settings, app_id, json_mode = resolve_app()
|
|
34
|
+
|
|
35
|
+
async def _run() -> BuildResponse:
|
|
36
|
+
async with ApiClient(settings) as client:
|
|
37
|
+
resp = await client.get(f"{base_path(app_id)}/builds/{build_id}")
|
|
38
|
+
handle_response_error(resp, resource="Build")
|
|
39
|
+
return BuildResponse.model_validate(resp.json())
|
|
40
|
+
|
|
41
|
+
build = run_api_call(_run())
|
|
42
|
+
|
|
43
|
+
if json_mode:
|
|
44
|
+
output(redacted_build(build), json_mode=True)
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
version = _version_label(build)
|
|
48
|
+
title = f"Build {version}" if version else "Build"
|
|
49
|
+
print_table(BUILD_TABLE_HEADERS, [format_build_row(build)], title=title)
|
|
50
|
+
print_fix_prompts([build])
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""`minitest draft`: ask Mini for a suite change and drive it to merge.
|
|
2
|
+
|
|
3
|
+
A draft is the workbench the webapp shows under Drafts: a branch of the suite plus
|
|
4
|
+
Mini's conversation about it. Mini is the only author of the branch; never `df apply`
|
|
5
|
+
into a draft's branch yourself.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from minitest_cli.commands import (
|
|
13
|
+
draft_asks,
|
|
14
|
+
draft_conversation,
|
|
15
|
+
draft_create,
|
|
16
|
+
draft_legacy,
|
|
17
|
+
draft_lifecycle,
|
|
18
|
+
draft_pr,
|
|
19
|
+
draft_update,
|
|
20
|
+
)
|
|
21
|
+
from minitest_cli.commands.draft_helpers import (
|
|
22
|
+
DRAFTS_PATH,
|
|
23
|
+
call,
|
|
24
|
+
draft_path,
|
|
25
|
+
resolve_target,
|
|
26
|
+
run_conductor,
|
|
27
|
+
)
|
|
28
|
+
from minitest_cli.commands.draft_render import (
|
|
29
|
+
DRAFT_TABLE_HEADERS,
|
|
30
|
+
draft_row,
|
|
31
|
+
show_draft,
|
|
32
|
+
)
|
|
33
|
+
from minitest_cli.commands.draft_watch import WatchUntil, watch
|
|
34
|
+
from minitest_cli.utils.output import (
|
|
35
|
+
print_info,
|
|
36
|
+
print_json,
|
|
37
|
+
print_table,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
app = typer.Typer(
|
|
41
|
+
name="draft",
|
|
42
|
+
help="Ask Mini for a suite change, follow the conversation, and check/merge the result.",
|
|
43
|
+
no_args_is_help=True,
|
|
44
|
+
)
|
|
45
|
+
EXIT_WATCH_TIMEOUT = 1
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@app.command(name="list")
|
|
49
|
+
def list_drafts(
|
|
50
|
+
history: Annotated[
|
|
51
|
+
bool, typer.Option("--history", help="List merged and discarded drafts instead.")
|
|
52
|
+
] = False,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""List the active app's drafts (open ones by default)."""
|
|
55
|
+
target = resolve_target()
|
|
56
|
+
params = {"appId": target.app_id, "scope": "history" if history else "open"}
|
|
57
|
+
body = call(target, "GET", DRAFTS_PATH, retry=True, params=params)
|
|
58
|
+
drafts = body.get("drafts", [])
|
|
59
|
+
if target.json_mode:
|
|
60
|
+
print_json(drafts)
|
|
61
|
+
return
|
|
62
|
+
if not drafts:
|
|
63
|
+
print_info("No drafts found.")
|
|
64
|
+
return
|
|
65
|
+
print_table(
|
|
66
|
+
DRAFT_TABLE_HEADERS, [draft_row(d) for d in drafts], title=f"Drafts ({len(drafts)})"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@app.command(name="get")
|
|
71
|
+
def get_draft(
|
|
72
|
+
draft_id: Annotated[str, typer.Argument(help="Draft ID.")],
|
|
73
|
+
watch_flag: Annotated[
|
|
74
|
+
bool, typer.Option("--watch", help="Poll until the draft is stable, then print it.")
|
|
75
|
+
] = False,
|
|
76
|
+
until: Annotated[
|
|
77
|
+
WatchUntil,
|
|
78
|
+
typer.Option(
|
|
79
|
+
"--until",
|
|
80
|
+
help="idle: Mini has nothing queued. settled: also no check or draft build running.",
|
|
81
|
+
),
|
|
82
|
+
] = WatchUntil.IDLE,
|
|
83
|
+
interval: Annotated[
|
|
84
|
+
float, typer.Option("--interval", min=1.0, help="Seconds between polls.")
|
|
85
|
+
] = 5.0,
|
|
86
|
+
timeout: Annotated[
|
|
87
|
+
float, typer.Option("--timeout", min=1.0, help="Give up after this many seconds (exit 1).")
|
|
88
|
+
] = 900.0,
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Show a draft: state, Mini's status, scenarios and open asks."""
|
|
91
|
+
target = resolve_target()
|
|
92
|
+
path = draft_path(draft_id)
|
|
93
|
+
if not watch_flag:
|
|
94
|
+
show_draft(call(target, "GET", path, retry=True), json_mode=target.json_mode)
|
|
95
|
+
return
|
|
96
|
+
draft, stable = run_conductor(
|
|
97
|
+
watch(target, path, until=until, interval=interval, timeout=timeout)
|
|
98
|
+
)
|
|
99
|
+
show_draft(draft, json_mode=target.json_mode)
|
|
100
|
+
if not stable:
|
|
101
|
+
print_info(f"Still not {until.value} after {timeout:.0f}s; run the same command again.")
|
|
102
|
+
raise typer.Exit(code=EXIT_WATCH_TIMEOUT)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
draft_create.register(app)
|
|
106
|
+
draft_update.register(app)
|
|
107
|
+
draft_conversation.register(app)
|
|
108
|
+
draft_lifecycle.register(app)
|
|
109
|
+
draft_pr.register(app)
|
|
110
|
+
app.add_typer(draft_asks.app)
|
|
111
|
+
draft_legacy.register(app)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""`minitest draft ask list|answer|decline`: answer what Mini asked on a draft.
|
|
2
|
+
|
|
3
|
+
Secret fields (``secret: true`` in an ask's answerFields) are only read from a file or
|
|
4
|
+
stdin (``--field-file KEY=PATH``, ``-`` for stdin) — never from the command line, where
|
|
5
|
+
shells and process lists keep them — and are never printed back.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Annotated, Any
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from minitest_cli.commands.draft_helpers import (
|
|
14
|
+
EXIT_GENERAL_ERROR,
|
|
15
|
+
EXIT_NOT_FOUND,
|
|
16
|
+
DraftTarget,
|
|
17
|
+
call,
|
|
18
|
+
draft_path,
|
|
19
|
+
resolve_target,
|
|
20
|
+
)
|
|
21
|
+
from minitest_cli.commands.draft_render import (
|
|
22
|
+
ASK_TABLE_HEADERS,
|
|
23
|
+
ask_fields,
|
|
24
|
+
read_file,
|
|
25
|
+
read_text,
|
|
26
|
+
show_draft,
|
|
27
|
+
)
|
|
28
|
+
from minitest_cli.commands.run_helpers import ensure_uuid
|
|
29
|
+
from minitest_cli.utils.output import (
|
|
30
|
+
print_error,
|
|
31
|
+
print_info,
|
|
32
|
+
print_json,
|
|
33
|
+
print_success,
|
|
34
|
+
print_table,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
app = typer.Typer(
|
|
38
|
+
name="ask", help="Read and answer Mini's questions on a draft.", no_args_is_help=True
|
|
39
|
+
)
|
|
40
|
+
DraftIdArg = Annotated[str, typer.Argument(help="Draft ID.")]
|
|
41
|
+
AskIdArg = Annotated[str, typer.Argument(help="Ask ID (from `draft ask list`).")]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _open_asks(target: DraftTarget, draft_id: str) -> list[dict[str, Any]]:
|
|
45
|
+
return call(target, "GET", draft_path(draft_id), retry=True).get("asks") or []
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _find_ask(target: DraftTarget, draft_id: str, ask_id: str) -> dict[str, Any]:
|
|
49
|
+
ensure_uuid(ask_id, kind="ask id")
|
|
50
|
+
for ask in _open_asks(target, draft_id):
|
|
51
|
+
if ask.get("id") == ask_id:
|
|
52
|
+
return ask
|
|
53
|
+
print_error(f"No open ask {ask_id} on draft {draft_id}; see `minitest draft ask list`.")
|
|
54
|
+
raise typer.Exit(code=EXIT_NOT_FOUND)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _pair(raw: str, flag: str) -> tuple[str, str]:
|
|
58
|
+
key, sep, value = raw.partition("=")
|
|
59
|
+
if not sep or not key:
|
|
60
|
+
print_error(f"{flag} expects KEY=VALUE.")
|
|
61
|
+
raise typer.Exit(code=EXIT_GENERAL_ERROR)
|
|
62
|
+
return key, value
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _fields(ask: dict[str, Any], inline: list[str], files: list[str]) -> dict[str, str]:
|
|
66
|
+
known = {f["key"]: f for f in ask.get("answerFields") or []}
|
|
67
|
+
values: dict[str, str] = {}
|
|
68
|
+
for raw in inline:
|
|
69
|
+
key, value = _pair(raw, "--field")
|
|
70
|
+
if known.get(key, {}).get("secret"):
|
|
71
|
+
print_error(f"'{key}' is secret: pass it with --field-file {key}=PATH (- = stdin).")
|
|
72
|
+
raise typer.Exit(code=EXIT_GENERAL_ERROR)
|
|
73
|
+
values[key] = value
|
|
74
|
+
if sum(1 for raw in files if _pair(raw, "--field-file")[1] == "-") > 1:
|
|
75
|
+
print_error("Only one --field-file can read stdin.")
|
|
76
|
+
raise typer.Exit(code=EXIT_GENERAL_ERROR)
|
|
77
|
+
for raw in files:
|
|
78
|
+
key, path = _pair(raw, "--field-file")
|
|
79
|
+
values[key] = read_file(Path(path)).removesuffix("\n")
|
|
80
|
+
unknown = sorted(set(values) - set(known))
|
|
81
|
+
if unknown:
|
|
82
|
+
print_error(f"Unknown field(s) {', '.join(unknown)}; this ask takes: {ask_fields(ask)}.")
|
|
83
|
+
raise typer.Exit(code=EXIT_GENERAL_ERROR)
|
|
84
|
+
return values
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@app.command(name="list")
|
|
88
|
+
def list_asks(draft_id: DraftIdArg) -> None:
|
|
89
|
+
"""List Mini's open questions on a draft, with the fields each answer takes."""
|
|
90
|
+
target = resolve_target()
|
|
91
|
+
asks = _open_asks(target, draft_id)
|
|
92
|
+
if target.json_mode:
|
|
93
|
+
print_json(asks)
|
|
94
|
+
return
|
|
95
|
+
if not asks:
|
|
96
|
+
print_info("Mini is not asking anything.")
|
|
97
|
+
return
|
|
98
|
+
rows = [[a["id"], a.get("askType", "-"), a["question"], ask_fields(a)] for a in asks]
|
|
99
|
+
print_table(ASK_TABLE_HEADERS, rows)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@app.command(name="answer")
|
|
103
|
+
def answer_ask(
|
|
104
|
+
draft_id: DraftIdArg,
|
|
105
|
+
ask_id: AskIdArg,
|
|
106
|
+
answer: Annotated[str | None, typer.Option("--answer", help="Free-text answer.")] = None,
|
|
107
|
+
answer_file: Annotated[
|
|
108
|
+
Path | None, typer.Option("--answer-file", help="Read the answer from a file (- = stdin).")
|
|
109
|
+
] = None,
|
|
110
|
+
field: Annotated[
|
|
111
|
+
list[str] | None, typer.Option("--field", help="KEY=VALUE for a non-secret field.")
|
|
112
|
+
] = None,
|
|
113
|
+
field_file: Annotated[
|
|
114
|
+
list[str] | None,
|
|
115
|
+
typer.Option("--field-file", help="KEY=PATH, value read from a file (- = stdin)."),
|
|
116
|
+
] = None,
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Answer an ask; Mini resumes the draft with it. Turns auto-merge off."""
|
|
119
|
+
text = read_text(answer, answer_file, flag="answer", required=False)
|
|
120
|
+
target = resolve_target()
|
|
121
|
+
ask = _find_ask(target, draft_id, ask_id)
|
|
122
|
+
values = _fields(ask, field or [], field_file or [])
|
|
123
|
+
if text is None and not values:
|
|
124
|
+
print_error("Pass --answer/--answer-file, or the ask's fields with --field/--field-file.")
|
|
125
|
+
raise typer.Exit(code=EXIT_GENERAL_ERROR)
|
|
126
|
+
body: dict[str, object] = {}
|
|
127
|
+
if text is not None:
|
|
128
|
+
body["answer"] = text
|
|
129
|
+
if values:
|
|
130
|
+
body["fields"] = values
|
|
131
|
+
draft = call(target, "POST", draft_path(draft_id, f"/asks/{ask_id}/answer"), json=body)
|
|
132
|
+
print_success("Answered; Mini picks it up next.")
|
|
133
|
+
show_draft(draft, json_mode=target.json_mode)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@app.command(name="decline")
|
|
137
|
+
def decline_ask(
|
|
138
|
+
draft_id: DraftIdArg,
|
|
139
|
+
ask_id: AskIdArg,
|
|
140
|
+
reason: Annotated[str | None, typer.Option("--reason", help="Why, so Mini can adapt.")] = None,
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Decline an ask; Mini carries on without it."""
|
|
143
|
+
target = resolve_target()
|
|
144
|
+
ensure_uuid(ask_id, kind="ask id")
|
|
145
|
+
body = {"reason": reason} if reason else None
|
|
146
|
+
draft = call(target, "POST", draft_path(draft_id, f"/asks/{ask_id}/decline"), json=body)
|
|
147
|
+
print_success("Declined.")
|
|
148
|
+
show_draft(draft, json_mode=target.json_mode)
|