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.
Files changed (226) hide show
  1. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.env.example +6 -0
  2. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/AGENTS.md +1 -1
  3. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/PKG-INFO +5 -26
  4. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/README.md +4 -25
  5. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/pyproject.toml +1 -1
  6. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/client.py +10 -4
  7. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill_helpers.py +10 -0
  8. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill_write.py +38 -2
  9. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps.py +4 -0
  10. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build.py +2 -0
  11. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build_fix_prompts.py +15 -10
  12. minitest_cli-0.37.0/src/minitest_cli/commands/build_get.py +50 -0
  13. minitest_cli-0.37.0/src/minitest_cli/commands/draft.py +111 -0
  14. minitest_cli-0.37.0/src/minitest_cli/commands/draft_asks.py +148 -0
  15. minitest_cli-0.37.0/src/minitest_cli/commands/draft_conversation.py +84 -0
  16. minitest_cli-0.37.0/src/minitest_cli/commands/draft_create.py +66 -0
  17. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature.py +9 -6
  18. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_apply.py +1 -1
  19. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_conflicts.py +1 -1
  20. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_helpers.py +1 -3
  21. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/draft_feature_show.py +1 -1
  22. minitest_cli-0.37.0/src/minitest_cli/commands/draft_helpers.py +131 -0
  23. minitest_cli-0.37.0/src/minitest_cli/commands/draft_legacy.py +40 -0
  24. minitest_cli-0.37.0/src/minitest_cli/commands/draft_lifecycle.py +103 -0
  25. minitest_cli-0.37.0/src/minitest_cli/commands/draft_pr.py +49 -0
  26. minitest_cli-0.37.0/src/minitest_cli/commands/draft_render.py +110 -0
  27. minitest_cli-0.37.0/src/minitest_cli/commands/draft_update.py +45 -0
  28. minitest_cli-0.37.0/src/minitest_cli/commands/draft_watch.py +90 -0
  29. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/env_helpers.py +4 -0
  30. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues.py +2 -0
  31. minitest_cli-0.37.0/src/minitest_cli/commands/issues_get.py +33 -0
  32. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile.py +5 -1
  33. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_helpers.py +9 -1
  34. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_list.py +3 -0
  35. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_update.py +2 -0
  36. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/config.py +5 -0
  37. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/main.py +2 -3
  38. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/build.py +4 -0
  39. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/issues.py +5 -0
  40. minitest_cli-0.37.0/tests/draft_backend.py +98 -0
  41. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_app_skill_commands.py +50 -0
  42. minitest_cli-0.37.0/tests/test_build_get_command.py +106 -0
  43. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_deprecated_commands.py +18 -3
  44. minitest_cli-0.37.0/tests/test_draft_ask_commands.py +120 -0
  45. minitest_cli-0.37.0/tests/test_draft_commands.py +167 -0
  46. minitest_cli-0.37.0/tests/test_draft_conversation_commands.py +133 -0
  47. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_draft_feature_commands.py +1 -1
  48. minitest_cli-0.37.0/tests/test_draft_pr_commands.py +135 -0
  49. minitest_cli-0.37.0/tests/test_issues_get_command.py +90 -0
  50. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_test_profile_commands.py +25 -0
  51. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/uv.lock +1 -1
  52. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance.py +0 -130
  53. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_callbacks.py +0 -86
  54. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_helpers.py +0 -132
  55. minitest_cli-0.35.0/src/minitest_cli/commands/maintenance_state.py +0 -56
  56. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.github/workflows/ci.yml +0 -0
  57. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.github/workflows/install-scripts.yml +0 -0
  58. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.github/workflows/release.yml +0 -0
  59. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.gitignore +0 -0
  60. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/.opencode/skill/release/SKILL.md +0 -0
  61. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/RELEASE.md +0 -0
  62. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/install.ps1 +0 -0
  63. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/install.sh +0 -0
  64. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/pyrightconfig.json +0 -0
  65. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/renovate.json +0 -0
  66. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/__init__.py +0 -0
  67. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/__init__.py +0 -0
  68. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/apps_manager_client.py +0 -0
  69. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/api/errors.py +0 -0
  70. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/assets/__init__.py +0 -0
  71. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/assets/callback.html +0 -0
  72. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/__init__.py +0 -0
  73. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/_response_errors.py +0 -0
  74. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_knowledge.py +0 -0
  75. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_knowledge_helpers.py +0 -0
  76. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_network_helpers.py +0 -0
  77. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill.py +0 -0
  78. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/app_skill_secret.py +0 -0
  79. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_dependencies.py +0 -0
  80. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_get.py +0 -0
  81. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_helpers.py +0 -0
  82. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/apps_table.py +0 -0
  83. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/auth.py +0 -0
  84. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/auth_api_key.py +0 -0
  85. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/batch.py +0 -0
  86. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/batch_helpers.py +0 -0
  87. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/batch_warnings.py +0 -0
  88. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build_commit.py +0 -0
  89. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/build_helpers.py +0 -0
  90. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/capabilities.py +0 -0
  91. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/commit_helpers.py +0 -0
  92. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/deprecated_alias.py +0 -0
  93. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/env.py +0 -0
  94. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/flow_types.py +0 -0
  95. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/flow_types_helpers.py +0 -0
  96. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/flow_types_write.py +0 -0
  97. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/init.py +0 -0
  98. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/init_helpers.py +0 -0
  99. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/init_playbook.py +0 -0
  100. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_fix.py +0 -0
  101. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_helpers.py +0 -0
  102. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_projection.py +0 -0
  103. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/issues_service.py +0 -0
  104. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/private_network.py +0 -0
  105. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/private_network_helpers.py +0 -0
  106. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/recording_timeline.py +0 -0
  107. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/residential_proxy.py +0 -0
  108. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run.py +0 -0
  109. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_cancel.py +0 -0
  110. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_commit.py +0 -0
  111. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_commit_helpers.py +0 -0
  112. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_display.py +0 -0
  113. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_feedback.py +0 -0
  114. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_helpers.py +0 -0
  115. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_recording.py +0 -0
  116. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_recording_helpers.py +0 -0
  117. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_tagged.py +0 -0
  118. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/run_targets.py +0 -0
  119. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens.py +0 -0
  120. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_detail.py +0 -0
  121. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_format.py +0 -0
  122. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_helpers.py +0 -0
  123. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/screens_tree.py +0 -0
  124. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/skill.py +0 -0
  125. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/tags.py +0 -0
  126. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/tags_helpers.py +0 -0
  127. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_file.py +0 -0
  128. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_file_helpers.py +0 -0
  129. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_file_list.py +0 -0
  130. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_card.py +0 -0
  131. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/test_profile_default.py +0 -0
  132. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/upgrade.py +0 -0
  133. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story.py +0 -0
  134. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_app_skills.py +0 -0
  135. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_bindings.py +0 -0
  136. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_camera.py +0 -0
  137. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_create.py +0 -0
  138. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_criteria.py +0 -0
  139. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_criterion_edits.py +0 -0
  140. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_delete.py +0 -0
  141. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_device_count.py +0 -0
  142. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_helpers.py +0 -0
  143. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_modify.py +0 -0
  144. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_overrides.py +0 -0
  145. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_profiles.py +0 -0
  146. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_setup_commit.py +0 -0
  147. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/user_story_update.py +0 -0
  148. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/verdicts.py +0 -0
  149. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/verdicts_helpers.py +0 -0
  150. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/commands/verdicts_projection.py +0 -0
  151. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/__init__.py +0 -0
  152. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/app_context.py +0 -0
  153. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/auth.py +0 -0
  154. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/credentials.py +0 -0
  155. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/oauth.py +0 -0
  156. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/tenants.py +0 -0
  157. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/core/token_exchange.py +0 -0
  158. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/__init__.py +0 -0
  159. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app.py +0 -0
  160. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app_env_vars.py +0 -0
  161. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app_failure.py +0 -0
  162. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/app_screen.py +0 -0
  163. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/base.py +0 -0
  164. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/batch.py +0 -0
  165. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/commit_build.py +0 -0
  166. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/draft_feature.py +0 -0
  167. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/flow_type.py +0 -0
  168. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/recording.py +0 -0
  169. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/story_run.py +0 -0
  170. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/tag.py +0 -0
  171. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/targets.py +0 -0
  172. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/models/user_story.py +0 -0
  173. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/__init__.py +0 -0
  174. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/confirm.py +0 -0
  175. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/dependency_sim.py +0 -0
  176. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/mermaid.py +0 -0
  177. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/output.py +0 -0
  178. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/skill_refresh.py +0 -0
  179. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/update_check.py +0 -0
  180. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/src/minitest_cli/utils/video_frames.py +0 -0
  181. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/__init__.py +0 -0
  182. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/_commit_transport.py +0 -0
  183. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/_network_settings_server.py +0 -0
  184. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/conftest.py +0 -0
  185. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_api_client_auth.py +0 -0
  186. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_app_knowledge_commands.py +0 -0
  187. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_apps_commands.py +0 -0
  188. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_apps_dependencies.py +0 -0
  189. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_auth.py +0 -0
  190. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_auth_api_key.py +0 -0
  191. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_auth_commands.py +0 -0
  192. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_batch_commands.py +0 -0
  193. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_build_commands.py +0 -0
  194. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_build_fix_prompts.py +0 -0
  195. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_capabilities_command.py +0 -0
  196. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_channel_header.py +0 -0
  197. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_code_quality.py +0 -0
  198. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_commit_commands.py +0 -0
  199. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_deprecated_flags.py +0 -0
  200. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_env.py +0 -0
  201. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_flow_types_commands.py +0 -0
  202. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_init_command.py +0 -0
  203. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_issues.py +0 -0
  204. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_issues_commands.py +0 -0
  205. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_mermaid.py +0 -0
  206. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_native_device_types.py +0 -0
  207. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_network_errors.py +0 -0
  208. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_private_network_commands.py +0 -0
  209. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_recording_timeline.py +0 -0
  210. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_residential_proxy_commands.py +0 -0
  211. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_response_errors.py +0 -0
  212. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_run_cancel.py +0 -0
  213. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_run_commands.py +0 -0
  214. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_run_frames_command.py +0 -0
  215. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_screens_commands.py +0 -0
  216. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_skill_command.py +0 -0
  217. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_suite_change_provenance.py +0 -0
  218. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_tags_commands.py +0 -0
  219. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_test_file_commands.py +0 -0
  220. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_upgrade_command.py +0 -0
  221. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_user_story_bindings_commands.py +0 -0
  222. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_user_story_commands.py +0 -0
  223. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_user_story_setup_commit.py +0 -0
  224. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_verdicts.py +0 -0
  225. {minitest_cli-0.35.0 → minitest_cli-0.37.0}/tests/test_verdicts_actionable.py +0 -0
  226. {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, 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.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` | Drafts — branches of the test suite |
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 df` | `minitest draft` |
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` | Drafts — branches of the test suite |
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 df` | `minitest draft` |
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 |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "minitest-cli"
3
- version = "0.35.0"
3
+ version = "0.37.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
 
@@ -40,9 +39,16 @@ class ApiClient:
40
39
  response = await client.get("/api/v1/apps")
41
40
  """
42
41
 
43
- def __init__(self, settings: Settings, *, token_override: str | None = None) -> None:
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._settings.api_url,
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
- 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])
@@ -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)