minitest-cli 0.34.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.34.0 → minitest_cli-0.36.0}/AGENTS.md +1 -1
  2. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/PKG-INFO +25 -20
  3. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/README.md +24 -19
  4. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/pyproject.toml +1 -1
  5. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/api/client.py +1 -2
  6. minitest_cli-0.36.0/src/minitest_cli/commands/app_network_helpers.py +114 -0
  7. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill_helpers.py +10 -0
  8. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill_write.py +38 -2
  9. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps.py +9 -2
  10. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build.py +2 -0
  11. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build_fix_prompts.py +15 -10
  12. minitest_cli-0.36.0/src/minitest_cli/commands/build_get.py +50 -0
  13. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues.py +2 -0
  14. minitest_cli-0.36.0/src/minitest_cli/commands/issues_get.py +33 -0
  15. minitest_cli-0.36.0/src/minitest_cli/commands/private_network.py +143 -0
  16. minitest_cli-0.36.0/src/minitest_cli/commands/private_network_helpers.py +93 -0
  17. minitest_cli-0.36.0/src/minitest_cli/commands/residential_proxy.py +121 -0
  18. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile.py +5 -1
  19. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_helpers.py +9 -1
  20. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_list.py +3 -0
  21. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_update.py +2 -0
  22. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/main.py +0 -2
  23. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/build.py +4 -0
  24. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/issues.py +5 -0
  25. minitest_cli-0.36.0/tests/_network_settings_server.py +106 -0
  26. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_app_skill_commands.py +50 -0
  27. minitest_cli-0.36.0/tests/test_build_get_command.py +106 -0
  28. minitest_cli-0.36.0/tests/test_issues_get_command.py +90 -0
  29. minitest_cli-0.36.0/tests/test_private_network_commands.py +118 -0
  30. minitest_cli-0.36.0/tests/test_residential_proxy_commands.py +89 -0
  31. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_test_profile_commands.py +25 -0
  32. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/uv.lock +1 -1
  33. minitest_cli-0.34.0/src/minitest_cli/commands/maintenance.py +0 -130
  34. minitest_cli-0.34.0/src/minitest_cli/commands/maintenance_callbacks.py +0 -86
  35. minitest_cli-0.34.0/src/minitest_cli/commands/maintenance_helpers.py +0 -132
  36. minitest_cli-0.34.0/src/minitest_cli/commands/maintenance_state.py +0 -56
  37. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/.env.example +0 -0
  38. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/.github/workflows/ci.yml +0 -0
  39. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/.github/workflows/install-scripts.yml +0 -0
  40. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/.github/workflows/release.yml +0 -0
  41. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/.gitignore +0 -0
  42. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/.opencode/skill/release/SKILL.md +0 -0
  43. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/RELEASE.md +0 -0
  44. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/install.ps1 +0 -0
  45. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/install.sh +0 -0
  46. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/pyrightconfig.json +0 -0
  47. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/renovate.json +0 -0
  48. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/__init__.py +0 -0
  49. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/api/__init__.py +0 -0
  50. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/api/apps_manager_client.py +0 -0
  51. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/api/errors.py +0 -0
  52. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/assets/__init__.py +0 -0
  53. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/assets/callback.html +0 -0
  54. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/__init__.py +0 -0
  55. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/_response_errors.py +0 -0
  56. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_knowledge.py +0 -0
  57. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_knowledge_helpers.py +0 -0
  58. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill.py +0 -0
  59. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/app_skill_secret.py +0 -0
  60. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_dependencies.py +0 -0
  61. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_get.py +0 -0
  62. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_helpers.py +0 -0
  63. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/apps_table.py +0 -0
  64. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/auth.py +0 -0
  65. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/auth_api_key.py +0 -0
  66. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/batch.py +0 -0
  67. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/batch_helpers.py +0 -0
  68. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/batch_warnings.py +0 -0
  69. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build_commit.py +0 -0
  70. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/build_helpers.py +0 -0
  71. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/capabilities.py +0 -0
  72. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/commit_helpers.py +0 -0
  73. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/deprecated_alias.py +0 -0
  74. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature.py +0 -0
  75. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_apply.py +0 -0
  76. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_conflicts.py +0 -0
  77. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_helpers.py +0 -0
  78. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/draft_feature_show.py +0 -0
  79. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/env.py +0 -0
  80. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/env_helpers.py +0 -0
  81. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/flow_types.py +0 -0
  82. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/flow_types_helpers.py +0 -0
  83. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/flow_types_write.py +0 -0
  84. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/init.py +0 -0
  85. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/init_helpers.py +0 -0
  86. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/init_playbook.py +0 -0
  87. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_fix.py +0 -0
  88. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_helpers.py +0 -0
  89. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_projection.py +0 -0
  90. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/issues_service.py +0 -0
  91. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/recording_timeline.py +0 -0
  92. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run.py +0 -0
  93. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_cancel.py +0 -0
  94. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_commit.py +0 -0
  95. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_commit_helpers.py +0 -0
  96. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_display.py +0 -0
  97. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_feedback.py +0 -0
  98. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_helpers.py +0 -0
  99. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_recording.py +0 -0
  100. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_recording_helpers.py +0 -0
  101. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_tagged.py +0 -0
  102. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/run_targets.py +0 -0
  103. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens.py +0 -0
  104. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_detail.py +0 -0
  105. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_format.py +0 -0
  106. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_helpers.py +0 -0
  107. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/screens_tree.py +0 -0
  108. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/skill.py +0 -0
  109. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/tags.py +0 -0
  110. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/tags_helpers.py +0 -0
  111. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_file.py +0 -0
  112. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_file_helpers.py +0 -0
  113. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_file_list.py +0 -0
  114. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_card.py +0 -0
  115. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/test_profile_default.py +0 -0
  116. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/upgrade.py +0 -0
  117. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story.py +0 -0
  118. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_app_skills.py +0 -0
  119. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_bindings.py +0 -0
  120. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_camera.py +0 -0
  121. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_create.py +0 -0
  122. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_criteria.py +0 -0
  123. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_criterion_edits.py +0 -0
  124. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_delete.py +0 -0
  125. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_device_count.py +0 -0
  126. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_helpers.py +0 -0
  127. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_modify.py +0 -0
  128. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_overrides.py +0 -0
  129. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_profiles.py +0 -0
  130. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_setup_commit.py +0 -0
  131. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/user_story_update.py +0 -0
  132. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/verdicts.py +0 -0
  133. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/verdicts_helpers.py +0 -0
  134. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/commands/verdicts_projection.py +0 -0
  135. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/__init__.py +0 -0
  136. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/app_context.py +0 -0
  137. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/auth.py +0 -0
  138. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/config.py +0 -0
  139. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/credentials.py +0 -0
  140. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/oauth.py +0 -0
  141. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/tenants.py +0 -0
  142. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/core/token_exchange.py +0 -0
  143. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/__init__.py +0 -0
  144. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app.py +0 -0
  145. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app_env_vars.py +0 -0
  146. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app_failure.py +0 -0
  147. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/app_screen.py +0 -0
  148. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/base.py +0 -0
  149. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/batch.py +0 -0
  150. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/commit_build.py +0 -0
  151. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/draft_feature.py +0 -0
  152. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/flow_type.py +0 -0
  153. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/recording.py +0 -0
  154. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/story_run.py +0 -0
  155. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/tag.py +0 -0
  156. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/targets.py +0 -0
  157. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/models/user_story.py +0 -0
  158. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/__init__.py +0 -0
  159. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/confirm.py +0 -0
  160. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/dependency_sim.py +0 -0
  161. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/mermaid.py +0 -0
  162. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/output.py +0 -0
  163. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/skill_refresh.py +0 -0
  164. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/update_check.py +0 -0
  165. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/src/minitest_cli/utils/video_frames.py +0 -0
  166. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/__init__.py +0 -0
  167. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/_commit_transport.py +0 -0
  168. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/conftest.py +0 -0
  169. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_api_client_auth.py +0 -0
  170. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_app_knowledge_commands.py +0 -0
  171. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_apps_commands.py +0 -0
  172. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_apps_dependencies.py +0 -0
  173. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_auth.py +0 -0
  174. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_auth_api_key.py +0 -0
  175. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_auth_commands.py +0 -0
  176. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_batch_commands.py +0 -0
  177. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_build_commands.py +0 -0
  178. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_build_fix_prompts.py +0 -0
  179. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_capabilities_command.py +0 -0
  180. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_channel_header.py +0 -0
  181. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_code_quality.py +0 -0
  182. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_commit_commands.py +0 -0
  183. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_deprecated_commands.py +0 -0
  184. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_deprecated_flags.py +0 -0
  185. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_draft_feature_commands.py +0 -0
  186. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_env.py +0 -0
  187. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_flow_types_commands.py +0 -0
  188. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_init_command.py +0 -0
  189. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_issues.py +0 -0
  190. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_issues_commands.py +0 -0
  191. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_mermaid.py +0 -0
  192. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_native_device_types.py +0 -0
  193. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_network_errors.py +0 -0
  194. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_recording_timeline.py +0 -0
  195. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_response_errors.py +0 -0
  196. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_run_cancel.py +0 -0
  197. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_run_commands.py +0 -0
  198. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_run_frames_command.py +0 -0
  199. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_screens_commands.py +0 -0
  200. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_skill_command.py +0 -0
  201. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_suite_change_provenance.py +0 -0
  202. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_tags_commands.py +0 -0
  203. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_test_file_commands.py +0 -0
  204. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_upgrade_command.py +0 -0
  205. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_user_story_bindings_commands.py +0 -0
  206. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_user_story_commands.py +0 -0
  207. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_user_story_setup_commit.py +0 -0
  208. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_verdicts.py +0 -0
  209. {minitest_cli-0.34.0 → minitest_cli-0.36.0}/tests/test_verdicts_actionable.py +0 -0
  210. {minitest_cli-0.34.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.34.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
@@ -166,7 +166,7 @@ app defaults.
166
166
  | Command | Description |
167
167
  | ---------------- | ------------------------- |
168
168
  | `minitest auth` | Authentication management |
169
- | `minitest apps` | List, inspect, and create apps |
169
+ | `minitest apps` | List, inspect, and create apps; residential proxy and private network settings |
170
170
  | `minitest scenario` | Create, update, list and delete scenarios |
171
171
  | `minitest scenario-binding` | Bind test accounts, test files or app skills to scenarios |
172
172
  | `minitest draft` | Drafts — branches of the test suite |
@@ -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
@@ -198,28 +197,34 @@ stderr and leave `--json` stdout unchanged. Switch to the new names.
198
197
  | `run cancel --srp`, `run recording --srp` | `--target-id` |
199
198
  | `--type` on `scenario create/update/list` | `--tag` |
200
199
 
201
- ## CLI-only maintenance
200
+ ## App network settings
202
201
 
203
- `minitest maintenance` lets a coding agent keep Minitest scenarios in sync
204
- without connecting GitHub. Run it from the app repository: the code stays on the
205
- machine, while the CLI sends only proposed scenario edits and the local HEAD SHA.
202
+ Two per-app settings decide how test devices reach the network. Anyone on the
203
+ app can read them; only workspace **owners and admins** can change them (other
204
+ roles get `Only workspace owners and admins can change this setting`, exit 2).
205
+ `set` reads the current settings first and changes only what you pass.
206
+ Domains are an exact host (`api.stg.example.com`) or `*.example.com`, which
207
+ matches subdomains only, not `example.com` itself.
206
208
 
207
209
  ```bash
208
- # Print the server-composed maintenance instructions for your coding agent
209
- minitest maintenance --agent
210
-
211
- # Agent workflow primitives
212
- minitest --json maintenance context
213
- minitest maintenance affected --file affected.json
214
- minitest maintenance change --file change.json
215
- minitest maintenance status --phase writing --message "Updating affected scenarios"
216
- minitest maintenance complete --changed
217
-
218
- # Apply proposed edits now, or open the web review queue
219
- minitest maintenance apply
220
- minitest maintenance apply --review
210
+ # Residential proxy: reach the listed domains from a residential IP in one country
211
+ minitest --app <app-id> apps residential-proxy show
212
+ minitest --app <app-id> apps residential-proxy set [--enable|--disable] [--country tr] \
213
+ [--domain ipinfo.io ...] [--remove-domain <d> ...] [--clear-domains]
214
+
215
+ # Private network: reach internal domains through a connector running in your network
216
+ minitest --app <app-id> apps private-network show
217
+ minitest --app <app-id> apps private-network connectors
218
+ minitest --app <app-id> apps private-network set [--enable|--disable] [--connector <name-or-id>] \
219
+ [--domain api.stg.corp.internal ...] [--remove-domain <d> ...] [--clear-domains] [--port 443 ...]
221
220
  ```
222
221
 
222
+ `--clear-domains --domain a --domain b` replaces the list; `--port` replaces the
223
+ ports (default 443). Turning the private network on needs a connector and at
224
+ least one domain. Connectors cannot be created from the CLI: a workspace owner
225
+ or admin creates them in the webapp, under **Workspace settings → Private
226
+ network**.
227
+
223
228
  ## Exit Codes
224
229
 
225
230
  | Code | Meaning |
@@ -133,7 +133,7 @@ app defaults.
133
133
  | Command | Description |
134
134
  | ---------------- | ------------------------- |
135
135
  | `minitest auth` | Authentication management |
136
- | `minitest apps` | List, inspect, and create apps |
136
+ | `minitest apps` | List, inspect, and create apps; residential proxy and private network settings |
137
137
  | `minitest scenario` | Create, update, list and delete scenarios |
138
138
  | `minitest scenario-binding` | Bind test accounts, test files or app skills to scenarios |
139
139
  | `minitest draft` | Drafts — branches of the test suite |
@@ -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
@@ -165,28 +164,34 @@ stderr and leave `--json` stdout unchanged. Switch to the new names.
165
164
  | `run cancel --srp`, `run recording --srp` | `--target-id` |
166
165
  | `--type` on `scenario create/update/list` | `--tag` |
167
166
 
168
- ## CLI-only maintenance
167
+ ## App network settings
169
168
 
170
- `minitest maintenance` lets a coding agent keep Minitest scenarios in sync
171
- without connecting GitHub. Run it from the app repository: the code stays on the
172
- machine, while the CLI sends only proposed scenario edits and the local HEAD SHA.
169
+ Two per-app settings decide how test devices reach the network. Anyone on the
170
+ app can read them; only workspace **owners and admins** can change them (other
171
+ roles get `Only workspace owners and admins can change this setting`, exit 2).
172
+ `set` reads the current settings first and changes only what you pass.
173
+ Domains are an exact host (`api.stg.example.com`) or `*.example.com`, which
174
+ matches subdomains only, not `example.com` itself.
173
175
 
174
176
  ```bash
175
- # Print the server-composed maintenance instructions for your coding agent
176
- minitest maintenance --agent
177
-
178
- # Agent workflow primitives
179
- minitest --json maintenance context
180
- minitest maintenance affected --file affected.json
181
- minitest maintenance change --file change.json
182
- minitest maintenance status --phase writing --message "Updating affected scenarios"
183
- minitest maintenance complete --changed
184
-
185
- # Apply proposed edits now, or open the web review queue
186
- minitest maintenance apply
187
- minitest maintenance apply --review
177
+ # Residential proxy: reach the listed domains from a residential IP in one country
178
+ minitest --app <app-id> apps residential-proxy show
179
+ minitest --app <app-id> apps residential-proxy set [--enable|--disable] [--country tr] \
180
+ [--domain ipinfo.io ...] [--remove-domain <d> ...] [--clear-domains]
181
+
182
+ # Private network: reach internal domains through a connector running in your network
183
+ minitest --app <app-id> apps private-network show
184
+ minitest --app <app-id> apps private-network connectors
185
+ minitest --app <app-id> apps private-network set [--enable|--disable] [--connector <name-or-id>] \
186
+ [--domain api.stg.corp.internal ...] [--remove-domain <d> ...] [--clear-domains] [--port 443 ...]
188
187
  ```
189
188
 
189
+ `--clear-domains --domain a --domain b` replaces the list; `--port` replaces the
190
+ ports (default 443). Turning the private network on needs a connector and at
191
+ least one domain. Connectors cannot be created from the CLI: a workspace owner
192
+ or admin creates them in the webapp, under **Workspace settings → Private
193
+ network**.
194
+
190
195
  ## Exit Codes
191
196
 
192
197
  | Code | Meaning |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "minitest-cli"
3
- version = "0.34.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
 
@@ -0,0 +1,114 @@
1
+ """Shared plumbing for the per-app network settings (residential proxy, private network)."""
2
+
3
+ import asyncio
4
+ import re
5
+ from typing import Any
6
+
7
+ import httpx
8
+ import typer
9
+
10
+ from minitest_cli.api.client import ApiClient
11
+ from minitest_cli.api.errors import format_network_error
12
+ from minitest_cli.commands._response_errors import extract_detail
13
+ from minitest_cli.core.app_context import resolve_app_id
14
+ from minitest_cli.core.auth import require_auth
15
+ from minitest_cli.core.config import Settings
16
+ from minitest_cli.utils.output import print_error
17
+
18
+ EXIT_GENERAL_ERROR = 1
19
+ EXIT_AUTH_ERROR = 2
20
+ EXIT_NETWORK_ERROR = 3
21
+ EXIT_NOT_FOUND = 4
22
+
23
+ ADMIN_ONLY_MESSAGE = "Only workspace owners and admins can change this setting."
24
+
25
+
26
+ def is_json_mode() -> bool:
27
+ return typer.Context.json_mode # type: ignore[attr-defined]
28
+
29
+
30
+ def resolve_target() -> tuple[Settings, str]:
31
+ settings: Settings = typer.Context.settings # type: ignore[attr-defined]
32
+ require_auth(settings)
33
+ return settings, resolve_app_id(settings, typer.Context.app_flag) # type: ignore[attr-defined]
34
+
35
+
36
+ def fail(message: str, code: int = EXIT_GENERAL_ERROR) -> typer.Exit:
37
+ print_error(message)
38
+ return typer.Exit(code=code)
39
+
40
+
41
+ def _raise_for_status(resp: httpx.Response) -> None:
42
+ if resp.status_code < 400:
43
+ return
44
+ detail = extract_detail(resp)
45
+ if resp.status_code == 403:
46
+ raise fail(f"{ADMIN_ONLY_MESSAGE} ({detail})", EXIT_AUTH_ERROR)
47
+ if resp.status_code == 401:
48
+ raise fail(f"Authentication failed: {detail}", EXIT_AUTH_ERROR)
49
+ if resp.status_code == 404:
50
+ raise fail(f"Not found: {detail}", EXIT_NOT_FOUND)
51
+ if resp.status_code in (400, 409, 422):
52
+ raise fail(f"Rejected by the server ({resp.status_code}): {detail}")
53
+ raise fail(f"API error ({resp.status_code}): {detail}", EXIT_NETWORK_ERROR)
54
+
55
+
56
+ def call_api(
57
+ settings: Settings, method: str, path: str, body: dict[str, Any] | None = None
58
+ ) -> dict[str, Any]:
59
+ async def _run() -> dict[str, Any]:
60
+ async with ApiClient(settings) as client:
61
+ resp = await client.request(method, path, json=body)
62
+ _raise_for_status(resp)
63
+ try:
64
+ return resp.json()
65
+ except ValueError as exc:
66
+ raise fail(
67
+ f"Unexpected non-JSON response ({resp.status_code}): {resp.text[:200]}",
68
+ EXIT_NETWORK_ERROR,
69
+ ) from exc
70
+
71
+ try:
72
+ return asyncio.run(_run())
73
+ except httpx.HTTPError as exc:
74
+ raise fail(format_network_error(exc), EXIT_NETWORK_ERROR) from exc
75
+
76
+
77
+ def merge_domains(
78
+ current: list[str],
79
+ *,
80
+ add: list[str] | None,
81
+ remove: list[str] | None,
82
+ clear: bool,
83
+ pattern: str,
84
+ max_domains: int,
85
+ ) -> list[str]:
86
+ """Apply ``--clear-domains``, ``--remove-domain`` then ``--domain`` and validate the result."""
87
+ added = [_normalize(d) for d in add or []]
88
+ invalid = [d for d in added if not re.fullmatch(pattern, d)]
89
+ if invalid:
90
+ raise fail(
91
+ f"Invalid domain(s): {', '.join(invalid)}. Use a lowercase host like api.example.com, "
92
+ "or *.example.com to match its subdomains (not example.com itself)."
93
+ )
94
+ removed = {_normalize(d) for d in remove or []}
95
+ unknown = sorted(removed - set(current))
96
+ if unknown:
97
+ raise fail(f"Not in the current domain list: {', '.join(unknown)}.")
98
+ base = [] if clear else [d for d in current if d not in removed]
99
+ merged = list(dict.fromkeys([*base, *added]))
100
+ if len(merged) > max_domains:
101
+ raise fail(f"Too many domains: {len(merged)} (max {max_domains}).")
102
+ return merged
103
+
104
+
105
+ def _normalize(domain: str) -> str:
106
+ return domain.strip().lower().rstrip(".")
107
+
108
+
109
+ def on_off(value: bool) -> str:
110
+ return "on" if value else "off"
111
+
112
+
113
+ def domain_list(domains: list[str]) -> str:
114
+ return ", ".join(domains) if domains else "-"
@@ -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(
@@ -1,4 +1,4 @@
1
- """App management commands: list, get, create, dependencies."""
1
+ """App management commands: list, get, create, dependencies, network settings."""
2
2
 
3
3
  import asyncio
4
4
  from pathlib import Path
@@ -10,7 +10,12 @@ import typer
10
10
  from minitest_cli.api.apps_manager_client import AppsManagerClient # noqa: F401 re-exported for tests
11
11
  from minitest_cli.api.client import ApiClient
12
12
  from minitest_cli.api.errors import format_network_error
13
- from minitest_cli.commands import apps_dependencies, apps_get
13
+ from minitest_cli.commands import (
14
+ apps_dependencies,
15
+ apps_get,
16
+ private_network,
17
+ residential_proxy,
18
+ )
14
19
  from minitest_cli.commands.apps_helpers import create_app_request
15
20
  from minitest_cli.commands.apps_table import APP_TABLE_HEADERS, app_row
16
21
  from minitest_cli.core.auth import require_auth
@@ -30,6 +35,8 @@ EXIT_NETWORK_ERROR = 3
30
35
  app = typer.Typer(name="apps", help="App management.")
31
36
  app.command(name="dependencies")(apps_dependencies.dependencies)
32
37
  app.command(name="get")(apps_get.get_app)
38
+ app.add_typer(residential_proxy.app)
39
+ app.add_typer(private_network.app)
33
40
 
34
41
 
35
42
  @app.callback()
@@ -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)))