minitest-cli 0.35.0__tar.gz → 0.36.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.
Files changed (210) hide show
  1. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/AGENTS.md +1 -1
  2. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/PKG-INFO +1 -24
  3. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/README.md +0 -23
  4. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/pyproject.toml +1 -1
  5. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/api/client.py +1 -2
  6. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill_helpers.py +10 -0
  7. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill_write.py +38 -2
  8. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build.py +2 -0
  9. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build_fix_prompts.py +15 -10
  10. minitest_cli-0.36.0/src/minitest_cli/commands/build_get.py +50 -0
  11. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues.py +2 -0
  12. minitest_cli-0.36.0/src/minitest_cli/commands/issues_get.py +33 -0
  13. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile.py +5 -1
  14. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_helpers.py +9 -1
  15. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_list.py +3 -0
  16. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_update.py +2 -0
  17. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/main.py +0 -2
  18. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/build.py +4 -0
  19. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/issues.py +5 -0
  20. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_app_skill_commands.py +50 -0
  21. minitest_cli-0.36.0/tests/test_build_get_command.py +106 -0
  22. minitest_cli-0.36.0/tests/test_issues_get_command.py +90 -0
  23. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_test_profile_commands.py +25 -0
  24. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/uv.lock +1 -1
  25. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance.py +0 -130
  26. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_callbacks.py +0 -86
  27. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_helpers.py +0 -132
  28. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_state.py +0 -56
  29. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/.env.example +0 -0
  30. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/.github/workflows/ci.yml +0 -0
  31. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/.github/workflows/install-scripts.yml +0 -0
  32. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/.github/workflows/release.yml +0 -0
  33. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/.gitignore +0 -0
  34. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/.opencode/skill/release/SKILL.md +0 -0
  35. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/RELEASE.md +0 -0
  36. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/install.ps1 +0 -0
  37. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/install.sh +0 -0
  38. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/pyrightconfig.json +0 -0
  39. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/renovate.json +0 -0
  40. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/__init__.py +0 -0
  41. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/api/__init__.py +0 -0
  42. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/api/apps_manager_client.py +0 -0
  43. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/api/errors.py +0 -0
  44. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/assets/__init__.py +0 -0
  45. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/assets/callback.html +0 -0
  46. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/__init__.py +0 -0
  47. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/_response_errors.py +0 -0
  48. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_knowledge.py +0 -0
  49. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_knowledge_helpers.py +0 -0
  50. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_network_helpers.py +0 -0
  51. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill.py +0 -0
  52. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill_secret.py +0 -0
  53. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps.py +0 -0
  54. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_dependencies.py +0 -0
  55. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_get.py +0 -0
  56. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_helpers.py +0 -0
  57. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_table.py +0 -0
  58. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/auth.py +0 -0
  59. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/auth_api_key.py +0 -0
  60. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/batch.py +0 -0
  61. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/batch_helpers.py +0 -0
  62. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/batch_warnings.py +0 -0
  63. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build_commit.py +0 -0
  64. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build_helpers.py +0 -0
  65. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/capabilities.py +0 -0
  66. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/commit_helpers.py +0 -0
  67. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/deprecated_alias.py +0 -0
  68. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature.py +0 -0
  69. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_apply.py +0 -0
  70. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_conflicts.py +0 -0
  71. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_helpers.py +0 -0
  72. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_show.py +0 -0
  73. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/env.py +0 -0
  74. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/env_helpers.py +0 -0
  75. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/flow_types.py +0 -0
  76. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/flow_types_helpers.py +0 -0
  77. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/flow_types_write.py +0 -0
  78. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/init.py +0 -0
  79. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/init_helpers.py +0 -0
  80. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/init_playbook.py +0 -0
  81. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_fix.py +0 -0
  82. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_helpers.py +0 -0
  83. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_projection.py +0 -0
  84. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_service.py +0 -0
  85. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/private_network.py +0 -0
  86. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/private_network_helpers.py +0 -0
  87. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/recording_timeline.py +0 -0
  88. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/residential_proxy.py +0 -0
  89. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run.py +0 -0
  90. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_cancel.py +0 -0
  91. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_commit.py +0 -0
  92. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_commit_helpers.py +0 -0
  93. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_display.py +0 -0
  94. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_feedback.py +0 -0
  95. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_helpers.py +0 -0
  96. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_recording.py +0 -0
  97. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_recording_helpers.py +0 -0
  98. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_tagged.py +0 -0
  99. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_targets.py +0 -0
  100. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens.py +0 -0
  101. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_detail.py +0 -0
  102. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_format.py +0 -0
  103. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_helpers.py +0 -0
  104. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_tree.py +0 -0
  105. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/skill.py +0 -0
  106. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/tags.py +0 -0
  107. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/tags_helpers.py +0 -0
  108. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_file.py +0 -0
  109. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_file_helpers.py +0 -0
  110. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_file_list.py +0 -0
  111. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_card.py +0 -0
  112. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_default.py +0 -0
  113. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/upgrade.py +0 -0
  114. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story.py +0 -0
  115. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_app_skills.py +0 -0
  116. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_bindings.py +0 -0
  117. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_camera.py +0 -0
  118. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_create.py +0 -0
  119. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_criteria.py +0 -0
  120. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_criterion_edits.py +0 -0
  121. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_delete.py +0 -0
  122. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_device_count.py +0 -0
  123. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_helpers.py +0 -0
  124. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_modify.py +0 -0
  125. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_overrides.py +0 -0
  126. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_profiles.py +0 -0
  127. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_setup_commit.py +0 -0
  128. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_update.py +0 -0
  129. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/verdicts.py +0 -0
  130. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/verdicts_helpers.py +0 -0
  131. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/verdicts_projection.py +0 -0
  132. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/__init__.py +0 -0
  133. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/app_context.py +0 -0
  134. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/auth.py +0 -0
  135. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/config.py +0 -0
  136. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/credentials.py +0 -0
  137. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/oauth.py +0 -0
  138. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/tenants.py +0 -0
  139. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/core/token_exchange.py +0 -0
  140. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/__init__.py +0 -0
  141. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app.py +0 -0
  142. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app_env_vars.py +0 -0
  143. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app_failure.py +0 -0
  144. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app_screen.py +0 -0
  145. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/base.py +0 -0
  146. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/batch.py +0 -0
  147. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/commit_build.py +0 -0
  148. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/draft_feature.py +0 -0
  149. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/flow_type.py +0 -0
  150. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/recording.py +0 -0
  151. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/story_run.py +0 -0
  152. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/tag.py +0 -0
  153. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/targets.py +0 -0
  154. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/models/user_story.py +0 -0
  155. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/__init__.py +0 -0
  156. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/confirm.py +0 -0
  157. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/dependency_sim.py +0 -0
  158. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/mermaid.py +0 -0
  159. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/output.py +0 -0
  160. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/skill_refresh.py +0 -0
  161. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/update_check.py +0 -0
  162. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/video_frames.py +0 -0
  163. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/__init__.py +0 -0
  164. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/_commit_transport.py +0 -0
  165. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/_network_settings_server.py +0 -0
  166. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/conftest.py +0 -0
  167. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_api_client_auth.py +0 -0
  168. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_app_knowledge_commands.py +0 -0
  169. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_apps_commands.py +0 -0
  170. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_apps_dependencies.py +0 -0
  171. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_auth.py +0 -0
  172. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_auth_api_key.py +0 -0
  173. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_auth_commands.py +0 -0
  174. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_batch_commands.py +0 -0
  175. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_build_commands.py +0 -0
  176. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_build_fix_prompts.py +0 -0
  177. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_capabilities_command.py +0 -0
  178. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_channel_header.py +0 -0
  179. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_code_quality.py +0 -0
  180. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_commit_commands.py +0 -0
  181. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_deprecated_commands.py +0 -0
  182. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_deprecated_flags.py +0 -0
  183. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_draft_feature_commands.py +0 -0
  184. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_env.py +0 -0
  185. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_flow_types_commands.py +0 -0
  186. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_init_command.py +0 -0
  187. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_issues.py +0 -0
  188. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_issues_commands.py +0 -0
  189. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_mermaid.py +0 -0
  190. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_native_device_types.py +0 -0
  191. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_network_errors.py +0 -0
  192. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_private_network_commands.py +0 -0
  193. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_recording_timeline.py +0 -0
  194. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_residential_proxy_commands.py +0 -0
  195. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_response_errors.py +0 -0
  196. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_run_cancel.py +0 -0
  197. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_run_commands.py +0 -0
  198. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_run_frames_command.py +0 -0
  199. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_screens_commands.py +0 -0
  200. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_skill_command.py +0 -0
  201. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_suite_change_provenance.py +0 -0
  202. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_tags_commands.py +0 -0
  203. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_test_file_commands.py +0 -0
  204. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_upgrade_command.py +0 -0
  205. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_user_story_bindings_commands.py +0 -0
  206. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_user_story_commands.py +0 -0
  207. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_user_story_setup_commit.py +0 -0
  208. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_verdicts.py +0 -0
  209. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_verdicts_actionable.py +0 -0
  210. {minitest_cli-0.35.0 → minitest_cli-0.36.0}/tests/test_version.py +0 -0
@@ -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, maintenance)
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.35.0
3
+ Version: 0.36.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
@@ -178,7 +178,6 @@ app defaults.
178
178
  | `minitest run` | Start runs and inspect their scenario runs |
179
179
  | `minitest batch` | List, inspect and cancel runs (a batch is a Run in the webapp) |
180
180
  | `minitest issues` | Read app issues and their fix prompts, mark them fixed |
181
- | `minitest maintenance` | CLI-only scenario maintenance against local code |
182
181
  | `minitest skill` | Print the CLI skill for your AI coding agent (not app skills) |
183
182
 
184
183
  ### Deprecated names
@@ -226,28 +225,6 @@ least one domain. Connectors cannot be created from the CLI: a workspace owner
226
225
  or admin creates them in the webapp, under **Workspace settings → Private
227
226
  network**.
228
227
 
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
228
  ## Exit Codes
252
229
 
253
230
  | Code | Meaning |
@@ -145,7 +145,6 @@ app defaults.
145
145
  | `minitest run` | Start runs and inspect their scenario runs |
146
146
  | `minitest batch` | List, inspect and cancel runs (a batch is a Run in the webapp) |
147
147
  | `minitest issues` | Read app issues and their fix prompts, mark them fixed |
148
- | `minitest maintenance` | CLI-only scenario maintenance against local code |
149
148
  | `minitest skill` | Print the CLI skill for your AI coding agent (not app skills) |
150
149
 
151
150
  ### Deprecated names
@@ -193,28 +192,6 @@ least one domain. Connectors cannot be created from the CLI: a workspace owner
193
192
  or admin creates them in the webapp, under **Workspace settings → Private
194
193
  network**.
195
194
 
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
195
  ## Exit Codes
219
196
 
220
197
  | Code | Meaning |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "minitest-cli"
3
- version = "0.35.0"
3
+ version = "0.36.0"
4
4
  description = "Minitest CLI – command-line interface for the Minitest testing platform"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -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 can block on server-side maintenance before it answers, and the
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
 
@@ -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(
@@ -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
- for item, build in zip(payload.get("items", []), result.items, strict=False):
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])
@@ -6,6 +6,7 @@ from typing import Annotated
6
6
  import typer
7
7
 
8
8
  from minitest_cli.commands.issues_fix import fix
9
+ from minitest_cli.commands.issues_get import get
9
10
  from minitest_cli.commands.issues_service import collect_issues
10
11
  from minitest_cli.commands.run_helpers import ensure_uuid, resolve_app, run_api_call
11
12
  from minitest_cli.utils.output import print_error, print_json
@@ -25,6 +26,7 @@ class IssueCriticality(StrEnum):
25
26
  app = typer.Typer(
26
27
  name="issues", help="Read app issues, fix prompts, and mark them fixed.", no_args_is_help=True
27
28
  )
29
+ app.command()(get)
28
30
  app.command()(fix)
29
31
 
30
32
 
@@ -0,0 +1,33 @@
1
+ """``minitest issues get``: read one issue 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.issues_helpers import fetch_failure, fetch_optional_batch
9
+ from minitest_cli.commands.issues_projection import build_block, issue_item
10
+ from minitest_cli.commands.run_helpers import ensure_uuid, resolve_app, run_api_call
11
+ from minitest_cli.core.config import Settings
12
+ from minitest_cli.models.issues import IssueDetailResponse
13
+ from minitest_cli.utils.output import print_json
14
+
15
+
16
+ async def read_issue(settings: Settings, app_id: str, issue_id: str) -> IssueDetailResponse:
17
+ async with ApiClient(settings) as client:
18
+ failure = await fetch_failure(client, app_id, issue_id)
19
+ batch = (
20
+ await fetch_optional_batch(client, app_id, failure.batch_id)
21
+ if failure.batch_id
22
+ else None
23
+ )
24
+ return IssueDetailResponse(issue=issue_item(failure), build=build_block(batch))
25
+
26
+
27
+ def get(
28
+ issue_id: Annotated[str, typer.Argument(help="Issue ID (the `id` from `issues list`).")],
29
+ ) -> None:
30
+ """Return one issue, whatever its status, with its build provenance and fix prompt as JSON."""
31
+ ensure_uuid(issue_id, kind="issue")
32
+ settings, app_id, _ = resolve_app()
33
+ print_json(run_api_call(read_issue(settings, app_id, issue_id)))
@@ -20,6 +20,7 @@ from minitest_cli.commands.test_profile_helpers import (
20
20
  is_json_mode,
21
21
  read_stdin_secret,
22
22
  run_api_call,
23
+ warn_if_qa_inbox_unreadable,
23
24
  )
24
25
  from minitest_cli.core.app_context import resolve_app_id
25
26
  from minitest_cli.core.auth import require_auth
@@ -125,6 +126,7 @@ def create_profile(
125
126
  data = run_api_call(_run())
126
127
  if not json_mode:
127
128
  print_success(f"Test profile created: {data.get('id', '')}")
129
+ warn_if_qa_inbox_unreadable(data)
128
130
  output(data, json_mode=json_mode)
129
131
 
130
132
 
@@ -143,7 +145,9 @@ def get_profile(
143
145
  handle_profile_response(resp)
144
146
  return resp.json()
145
147
 
146
- output(run_api_call(_run()), json_mode=json_mode)
148
+ data = run_api_call(_run())
149
+ warn_if_qa_inbox_unreadable(data)
150
+ output(data, json_mode=json_mode)
147
151
 
148
152
 
149
153
  @app.command(name="delete")
@@ -13,7 +13,7 @@ from minitest_cli.commands.user_story_helpers import (
13
13
  is_json_mode,
14
14
  run_api_call,
15
15
  )
16
- from minitest_cli.utils.output import print_error
16
+ from minitest_cli.utils.output import print_error, print_warning
17
17
 
18
18
  EXIT_NETWORK_ERROR = 3
19
19
  EXIT_NOT_FOUND = 4
@@ -87,3 +87,11 @@ __all__ = [
87
87
  "read_stdin_secret",
88
88
  "run_api_call",
89
89
  ]
90
+
91
+
92
+ def warn_if_qa_inbox_unreadable(profile: dict[str, Any]) -> None:
93
+ if profile.get("qaInboxShared"):
94
+ print_warning(
95
+ f"{profile.get('username')} belongs to another workspace, so Mini can't read its "
96
+ "emails (login codes, verification links). Use a different @qa.minitap.ai address."
97
+ )
@@ -13,6 +13,7 @@ from minitest_cli.commands.test_profile_helpers import (
13
13
  handle_profile_response,
14
14
  is_json_mode,
15
15
  run_api_call,
16
+ warn_if_qa_inbox_unreadable,
16
17
  )
17
18
  from minitest_cli.core.app_context import resolve_app_id
18
19
  from minitest_cli.core.auth import require_auth
@@ -36,6 +37,8 @@ def register(app: typer.Typer) -> None:
36
37
 
37
38
  data = run_api_call(_run())
38
39
  items = data["items"] if isinstance(data, dict) and "items" in data else data
40
+ for profile in items:
41
+ warn_if_qa_inbox_unreadable(profile)
39
42
  if json_mode:
40
43
  output(items, json_mode=True)
41
44
  return
@@ -20,6 +20,7 @@ from minitest_cli.commands.test_profile_helpers import (
20
20
  is_json_mode,
21
21
  read_stdin_secret,
22
22
  run_api_call,
23
+ warn_if_qa_inbox_unreadable,
23
24
  )
24
25
  from minitest_cli.core.app_context import resolve_app_id
25
26
  from minitest_cli.core.auth import require_auth
@@ -156,4 +157,5 @@ def register(app: typer.Typer) -> None:
156
157
  data = run_api_call(_run())
157
158
  if not json_mode:
158
159
  print_success(f"Test profile updated: {profile_id}")
160
+ warn_if_qa_inbox_unreadable(data)
159
161
  output(data, json_mode=json_mode)
@@ -18,7 +18,6 @@ from minitest_cli.commands import (
18
18
  flow_types,
19
19
  init,
20
20
  issues,
21
- maintenance,
22
21
  run,
23
22
  screens,
24
23
  skill,
@@ -60,7 +59,6 @@ app.add_typer(build.app)
60
59
  app.add_typer(env.app)
61
60
  app.add_typer(run.app)
62
61
  app.add_typer(issues.app)
63
- app.add_typer(maintenance.app)
64
62
  app.add_typer(batch.app)
65
63
  app.add_typer(skill.app)
66
64
  app.add_typer(upgrade.app)
@@ -17,6 +17,10 @@ class BuildResponse(CamelModel):
17
17
  size_bytes: int | None = None
18
18
  commit_sha: str | None = None
19
19
  commit_title: str | None = None
20
+ app_version: str | None = None
21
+ build_number: str | None = None
22
+ verification_status: str | None = None
23
+ verification_reason: str | None = None
20
24
  error_class: str | None = None
21
25
  error_summary: str | None = None
22
26
  error_remediation: str | None = None
@@ -75,3 +75,8 @@ class IssuesListResponse(CamelModel):
75
75
  scope: IssuesScope
76
76
  build: IssuesBuild
77
77
  issues: list[IssueItem]
78
+
79
+
80
+ class IssueDetailResponse(CamelModel):
81
+ issue: IssueItem
82
+ build: IssuesBuild
@@ -97,6 +97,56 @@ def test_update_all_platforms_clears_the_platform_filter(tmp_path):
97
97
  assert recorder.body == {"platforms": []}
98
98
 
99
99
 
100
+ def test_update_sends_paired_replacements_and_prints_what_changed(tmp_path):
101
+ changes = ["Changed runnable blocks: fill.sh", "Instructions went from 900 to 940 characters"]
102
+ recorder = Recorder(httpx.Response(200, json=_skill(version=3, changes=changes)))
103
+
104
+ result = _invoke(
105
+ skill_app,
106
+ [
107
+ "update",
108
+ "color-by-number-fill",
109
+ "--replace",
110
+ "retry once",
111
+ "--with",
112
+ "retry three times",
113
+ "--replace",
114
+ "# old check\n",
115
+ "--with",
116
+ "",
117
+ "--allow-destructive",
118
+ ],
119
+ recorder,
120
+ tmp_path,
121
+ )
122
+
123
+ assert result.exit_code == 0, result.output
124
+ assert recorder.body == {
125
+ "edits": [
126
+ {"oldText": "retry once", "newText": "retry three times"},
127
+ {"oldText": "# old check\n", "newText": ""},
128
+ ],
129
+ "allowDestructive": True,
130
+ }
131
+ assert "Changed runnable blocks: fill.sh" in result.output
132
+
133
+
134
+ @pytest.mark.parametrize(
135
+ "args",
136
+ [
137
+ ["--replace", "a", "--with", "b", "--replace", "c"],
138
+ ["--replace", "a", "--with", "b", "--instructions", "whole"],
139
+ ],
140
+ )
141
+ def test_update_refuses_ambiguous_instruction_changes_before_calling_the_api(tmp_path, args):
142
+ recorder = Recorder(httpx.Response(200, json=_skill()))
143
+
144
+ result = _invoke(skill_app, ["update", "color-by-number-fill", *args], recorder, tmp_path)
145
+
146
+ assert result.exit_code == 1
147
+ assert recorder.requests == []
148
+
149
+
100
150
  def test_delete_linked_skill_lists_scenarios_and_keeps_the_skill(tmp_path):
101
151
  recorder = Recorder(
102
152
  httpx.Response(
@@ -0,0 +1,106 @@
1
+ """Behavior tests for `minitest build get`."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any
6
+ from unittest.mock import patch
7
+
8
+ import httpx
9
+ from tests._commit_transport import make_settings, routed
10
+ from typer.testing import CliRunner
11
+
12
+ from minitest_cli.commands.build_fix_prompts import WITHHELD_NOTICE
13
+ from minitest_cli.main import app
14
+
15
+ runner = CliRunner()
16
+ APP_ID = "a0d9820f-5136-4f70-b46b-e5966f56bfb5"
17
+ BUILD_ID = "6f1c2a3b-4d5e-4f60-8a7b-9c0d1e2f3a4b"
18
+ FOREIGN_BUILD_ID = "0b6d1f4e-2c3a-4e5f-9a8b-7c6d5e4f3a2b"
19
+ BUILD = {
20
+ "id": BUILD_ID,
21
+ "appId": APP_ID,
22
+ "kind": "native",
23
+ "platform": "ios",
24
+ "status": "completed",
25
+ "originalName": "Shop.app.zip",
26
+ "sizeBytes": 2048,
27
+ "commitSha": "a1b2c3d4e5f6",
28
+ "commitTitle": "feat: checkout v2",
29
+ "appVersion": "4.2.0",
30
+ "buildNumber": "317",
31
+ "verificationStatus": "verified",
32
+ "createdAt": "2026-10-14T09:30:00Z",
33
+ }
34
+
35
+
36
+ def _serve(build: dict[str, Any]):
37
+ seen: list[str] = []
38
+
39
+ def handler(request: httpx.Request) -> httpx.Response:
40
+ seen.append(request.url.path)
41
+ if request.url.path == f"/api/v1/apps/{APP_ID}/builds/{BUILD_ID}":
42
+ return httpx.Response(200, json=build)
43
+ return httpx.Response(404, json={"message": "Build not found"})
44
+
45
+ return handler, seen
46
+
47
+
48
+ def _invoke(tmp_path: Path, build: dict[str, Any], args: list[str]):
49
+ handler, seen = _serve(build)
50
+ with (
51
+ routed(handler),
52
+ patch("minitest_cli.main.get_settings", return_value=make_settings(tmp_path)),
53
+ patch("minitest_cli.main.check_for_updates"),
54
+ ):
55
+ return runner.invoke(app, args), seen
56
+
57
+
58
+ class TestBuildGet:
59
+ def test_get_returns_the_app_build_as_json(self, tmp_path: Path) -> None:
60
+ result, seen = _invoke(tmp_path, BUILD, ["--json", "build", "get", BUILD_ID])
61
+
62
+ assert result.exit_code == 0
63
+ assert seen == [f"/api/v1/apps/{APP_ID}/builds/{BUILD_ID}"]
64
+ payload = json.loads(result.stdout)
65
+ assert payload["id"] == BUILD_ID
66
+ assert (payload["appVersion"], payload["buildNumber"]) == ("4.2.0", "317")
67
+ assert payload["commitTitle"] == "feat: checkout v2"
68
+ assert payload["verificationStatus"] == "verified"
69
+ assert payload["guidance"] is None
70
+
71
+ def test_get_withholds_infra_failure_details(self, tmp_path: Path) -> None:
72
+ failed = {
73
+ **BUILD,
74
+ "status": "failed",
75
+ "errorClass": "infra",
76
+ "errorSummary": "builder disk full",
77
+ "errorFixPrompt": "ssh into the builder",
78
+ }
79
+
80
+ result, _ = _invoke(tmp_path, failed, ["--json", "build", "get", BUILD_ID])
81
+
82
+ payload = json.loads(result.stdout)
83
+ assert payload["guidance"] == {"source": "withheld", "text": WITHHELD_NOTICE}
84
+ assert payload["errorSummary"] == WITHHELD_NOTICE
85
+ assert payload["errorFixPrompt"] is None
86
+
87
+ def test_get_shows_the_version_in_human_mode(self, tmp_path: Path) -> None:
88
+ result, _ = _invoke(tmp_path, BUILD, ["build", "get", BUILD_ID])
89
+
90
+ assert result.exit_code == 0
91
+ assert "Build 4.2.0 (317)" in result.stdout
92
+ assert "completed" in result.stdout
93
+
94
+ def test_get_a_build_of_another_app_is_not_found(self, tmp_path: Path) -> None:
95
+ result, _ = _invoke(tmp_path, BUILD, ["--json", "build", "get", FOREIGN_BUILD_ID])
96
+
97
+ assert result.exit_code == 4
98
+ assert "Build not found" in result.stderr
99
+ assert result.stdout == ""
100
+
101
+ def test_get_rejects_a_non_uuid_without_calling_the_api(self, tmp_path: Path) -> None:
102
+ result, seen = _invoke(tmp_path, BUILD, ["build", "get", "317"])
103
+
104
+ assert result.exit_code == 1
105
+ assert "not a valid UUID" in result.stderr
106
+ assert seen == []