wade-cli 0.30.2__tar.gz → 0.33.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 (291) hide show
  1. {wade_cli-0.30.2 → wade_cli-0.33.0}/.wade.yml +22 -23
  2. {wade_cli-0.30.2 → wade_cli-0.33.0}/AGENTS.md +1 -1
  3. {wade_cli-0.30.2 → wade_cli-0.33.0}/CHANGELOG.md +42 -0
  4. {wade_cli-0.30.2 → wade_cli-0.33.0}/KNOWLEDGE.md +86 -2
  5. wade_cli-0.33.0/KNOWLEDGE.ratings.yml +62 -0
  6. {wade_cli-0.30.2 → wade_cli-0.33.0}/PKG-INFO +16 -4
  7. {wade_cli-0.30.2 → wade_cli-0.33.0}/README.md +14 -2
  8. {wade_cli-0.30.2 → wade_cli-0.33.0}/docs/dev/architecture.md +4 -5
  9. {wade_cli-0.30.2 → wade_cli-0.33.0}/docs/dev/skills-system.md +2 -3
  10. {wade_cli-0.30.2 → wade_cli-0.33.0}/pyproject.toml +2 -2
  11. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/__init__.py +1 -1
  12. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/hooks/plan_write_guard.py +4 -5
  13. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/hooks/worktree_guard.py +4 -5
  14. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/delegation.py +1 -1
  15. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/task.py +12 -0
  16. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/providers/base.py +6 -3
  17. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/providers/clickup.py +7 -2
  18. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/providers/github.py +8 -4
  19. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/providers/markdown.py +5 -1
  20. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/ai_resolution.py +12 -3
  21. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/check_service.py +51 -12
  22. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/delegation_service.py +1 -19
  23. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/deps_service.py +4 -0
  24. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/implementation_service/__init__.py +6 -0
  25. wade_cli-0.33.0/src/wade/services/implementation_service/_shared.py +55 -0
  26. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/implementation_service/batch.py +1 -1
  27. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/implementation_service/bootstrap.py +2 -12
  28. wade_cli-0.33.0/src/wade/services/implementation_service/cleanup.py +370 -0
  29. wade_cli-0.33.0/src/wade/services/implementation_service/core.py +772 -0
  30. wade_cli-0.33.0/src/wade/services/implementation_service/done.py +488 -0
  31. wade_cli-0.33.0/src/wade/services/implementation_service/draft_pr.py +191 -0
  32. wade_cli-0.33.0/src/wade/services/implementation_service/lifecycle.py +472 -0
  33. wade_cli-0.33.0/src/wade/services/implementation_service/sync.py +722 -0
  34. wade_cli-0.33.0/src/wade/services/init_service/__init__.py +16 -0
  35. wade_cli-0.33.0/src/wade/services/init_service/auth.py +96 -0
  36. wade_cli-0.33.0/src/wade/services/init_service/commands.py +707 -0
  37. wade_cli-0.33.0/src/wade/services/init_service/config_io.py +516 -0
  38. wade_cli-0.33.0/src/wade/services/init_service/manifest.py +146 -0
  39. wade_cli-0.33.0/src/wade/services/init_service/migrations.py +354 -0
  40. wade_cli-0.33.0/src/wade/services/init_service/prompts_ai.py +339 -0
  41. wade_cli-0.33.0/src/wade/services/init_service/prompts_setup.py +675 -0
  42. wade_cli-0.33.0/src/wade/services/init_service/shell.py +185 -0
  43. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/plan_service.py +123 -26
  44. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/prompt_delivery.py +1 -1
  45. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/review_delegation_service.py +1 -0
  46. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/skills/installer.py +1 -6
  47. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/ui/console.py +1 -1
  48. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/install.py +23 -1
  49. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/implementation-session/SKILL.md +1 -1
  50. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_implement_work_done_contract.py +1 -1
  51. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_autostash.py +9 -9
  52. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_implementation_lifecycle.py +15 -7
  53. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_skill_install.py +2 -2
  54. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/live/test_wade_live_ai_taskr.py +0 -1
  55. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_effort.py +8 -2
  56. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_hooks/test_plan_write_guard.py +1 -1
  57. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_hooks/test_worktree_guard.py +1 -1
  58. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_delegation.py +1 -1
  59. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_providers/test_clickup.py +14 -0
  60. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_providers/test_github.py +22 -0
  61. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_providers/test_markdown.py +9 -0
  62. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_ai_resolution.py +15 -0
  63. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_bootstrap_allowlist.py +1 -8
  64. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_catchup.py +28 -28
  65. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_check.py +8 -0
  66. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_deps_service.py +54 -1
  67. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_done_plan_flag.py +12 -12
  68. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_done_via_direct_cleanup.py +27 -27
  69. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_implementation_done_sync.py +194 -90
  70. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_implementation_service.py +74 -34
  71. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_init.py +158 -80
  72. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_init_shell_integration.py +14 -14
  73. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_plan_service.py +354 -4
  74. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_post_implementation_lifecycle.py +52 -52
  75. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_pr_summary_path.py +8 -8
  76. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_preserve_session_data.py +16 -16
  77. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_prompt_delivery.py +1 -1
  78. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_smart_start_resume.py +1 -1
  79. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_sync_base_branch.py +12 -12
  80. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_install.py +45 -0
  81. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_markdown.py +1 -1
  82. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_yolo.py +26 -40
  83. wade_cli-0.30.2/KNOWLEDGE.ratings.yml +0 -30
  84. wade_cli-0.30.2/src/wade/services/implementation_service/core.py +0 -2869
  85. wade_cli-0.30.2/src/wade/services/init_service.py +0 -2770
  86. wade_cli-0.30.2/tests/fixtures/transcripts/gemini_session.txt +0 -30
  87. {wade_cli-0.30.2 → wade_cli-0.33.0}/.github/workflows/auto-version.yml +0 -0
  88. {wade_cli-0.30.2 → wade_cli-0.33.0}/.github/workflows/ci.yml +0 -0
  89. {wade_cli-0.30.2 → wade_cli-0.33.0}/.github/workflows/pr-title-lint.yml +0 -0
  90. {wade_cli-0.30.2 → wade_cli-0.33.0}/.github/workflows/publish.yml +0 -0
  91. {wade_cli-0.30.2 → wade_cli-0.33.0}/.github/workflows/release.yml +0 -0
  92. {wade_cli-0.30.2 → wade_cli-0.33.0}/.gitignore +0 -0
  93. {wade_cli-0.30.2 → wade_cli-0.33.0}/.pre-commit-config.yaml +0 -0
  94. {wade_cli-0.30.2 → wade_cli-0.33.0}/CONTRIBUTING.md +0 -0
  95. {wade_cli-0.30.2 → wade_cli-0.33.0}/assets/wade.png +0 -0
  96. {wade_cli-0.30.2 → wade_cli-0.33.0}/docker-compose.e2e.yml +0 -0
  97. {wade_cli-0.30.2 → wade_cli-0.33.0}/docs/dev/documentation-policies.md +0 -0
  98. {wade_cli-0.30.2 → wade_cli-0.33.0}/docs/dev/extending.md +0 -0
  99. {wade_cli-0.30.2 → wade_cli-0.33.0}/docs/dev/testing.md +0 -0
  100. {wade_cli-0.30.2 → wade_cli-0.33.0}/install.sh +0 -0
  101. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/auto_version.py +0 -0
  102. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/changelog.py +0 -0
  103. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/check-all.sh +0 -0
  104. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/check.sh +0 -0
  105. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/fmt.sh +0 -0
  106. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/hooks/pre-push +0 -0
  107. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/install-hooks.sh +0 -0
  108. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/release.sh +0 -0
  109. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/setup-worktree.sh +0 -0
  110. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/test-e2e-docker.sh +0 -0
  111. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/test-e2e.sh +0 -0
  112. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/test-live-ai-taskr.sh +0 -0
  113. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/test-live-ai.sh +0 -0
  114. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/test-live-gh.sh +0 -0
  115. {wade_cli-0.30.2 → wade_cli-0.33.0}/scripts/test.sh +0 -0
  116. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/__main__.py +0 -0
  117. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/__init__.py +0 -0
  118. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/admin.py +0 -0
  119. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/autocomplete.py +0 -0
  120. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/implementation_session.py +0 -0
  121. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/knowledge.py +0 -0
  122. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/main.py +0 -0
  123. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/plan_session.py +0 -0
  124. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/review.py +0 -0
  125. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/review_pr_comments_session.py +0 -0
  126. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/session_shared.py +0 -0
  127. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/task.py +0 -0
  128. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/cli/worktree.py +0 -0
  129. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/config/__init__.py +0 -0
  130. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/config/loader.py +0 -0
  131. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/config/migrations.py +0 -0
  132. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/config/schema.py +0 -0
  133. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/db/__init__.py +0 -0
  134. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/db/engine.py +0 -0
  135. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/db/repositories.py +0 -0
  136. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/db/tables.py +0 -0
  137. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/git/__init__.py +0 -0
  138. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/git/branch.py +0 -0
  139. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/git/pr.py +0 -0
  140. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/git/repo.py +0 -0
  141. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/git/stash.py +0 -0
  142. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/git/sync.py +0 -0
  143. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/git/worktree.py +0 -0
  144. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/hooks/__init__.py +0 -0
  145. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/logging/__init__.py +0 -0
  146. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/logging/context.py +0 -0
  147. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/logging/setup.py +0 -0
  148. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/__init__.py +0 -0
  149. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/batch.py +0 -0
  150. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/config.py +0 -0
  151. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/deps.py +0 -0
  152. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/events.py +0 -0
  153. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/review.py +0 -0
  154. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/models/session.py +0 -0
  155. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/providers/__init__.py +0 -0
  156. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/providers/_pr_delegate.py +0 -0
  157. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/providers/registry.py +0 -0
  158. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/__init__.py +0 -0
  159. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/batch_review_service.py +0 -0
  160. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/implementation_service/usage_tracking.py +0 -0
  161. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/knowledge_search.py +0 -0
  162. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/knowledge_service.py +0 -0
  163. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/review_service.py +0 -0
  164. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/review_settle.py +0 -0
  165. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/smart_start.py +0 -0
  166. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/services/task_service.py +0 -0
  167. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/skills/__init__.py +0 -0
  168. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/skills/pointer.py +0 -0
  169. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/ui/__init__.py +0 -0
  170. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/ui/prompts.py +0 -0
  171. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/__init__.py +0 -0
  172. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/clipboard.py +0 -0
  173. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/http.py +0 -0
  174. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/markdown.py +0 -0
  175. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/process.py +0 -0
  176. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/slug.py +0 -0
  177. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/terminal.py +0 -0
  178. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/token_usage_markdown.py +0 -0
  179. {wade_cli-0.30.2 → wade_cli-0.33.0}/src/wade/utils/update_check.py +0 -0
  180. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/agents-pointer.md +0 -0
  181. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/prompts/deps-analysis.md +0 -0
  182. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/prompts/implement-context.md +0 -0
  183. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/prompts/plan-session.md +0 -0
  184. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/prompts/review-batch.md +0 -0
  185. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/prompts/review-code.md +0 -0
  186. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/prompts/review-plan.md +0 -0
  187. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/prompts/review-pr-comments.md +0 -0
  188. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/setup-worktree.sh.example +0 -0
  189. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/_partials/review-enforcement-rule.md +0 -0
  190. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/_partials/review-implementation-closing-step.md +0 -0
  191. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/_partials/review-plan-step.md +0 -0
  192. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/_partials/user-interaction.md +0 -0
  193. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/deps/SKILL.md +0 -0
  194. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/knowledge/SKILL.md +0 -0
  195. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/plan-session/SKILL.md +0 -0
  196. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/review-pr-comments-session/SKILL.md +0 -0
  197. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/task/SKILL.md +0 -0
  198. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/task/examples.md +0 -0
  199. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/skills/task/plan-format.md +0 -0
  200. {wade_cli-0.30.2 → wade_cli-0.33.0}/templates/statusline-command.sh +0 -0
  201. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/__init__.py +0 -0
  202. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/conftest.py +0 -0
  203. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/__init__.py +0 -0
  204. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/_support.py +0 -0
  205. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/conftest.py +0 -0
  206. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/mock_gh_script.py +0 -0
  207. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_admin_contract.py +0 -0
  208. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_check_contract.py +0 -0
  209. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_knowledge_contract.py +0 -0
  210. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_mock_gh_contract.py +0 -0
  211. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_plan_contract.py +0 -0
  212. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_review_contract.py +0 -0
  213. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_smart_start_contract.py +0 -0
  214. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_task_contract.py +0 -0
  215. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/e2e/test_work_sync_list_contract.py +0 -0
  216. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/fixtures/live/taskr_header_hi_body.md +0 -0
  217. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/fixtures/live/taskr_header_howdy_body.md +0 -0
  218. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/fixtures/transcripts/claude_session.txt +0 -0
  219. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/fixtures/transcripts/codex_session.txt +0 -0
  220. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/fixtures/transcripts/copilot_session.txt +0 -0
  221. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/fixtures/transcripts/generic_session.txt +0 -0
  222. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/__init__.py +0 -0
  223. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_check.py +0 -0
  224. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_git.py +0 -0
  225. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_init.py +0 -0
  226. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_knowledge.py +0 -0
  227. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/integration/test_markdown_provider.py +0 -0
  228. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/live/__init__.py +0 -0
  229. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/live/test_wade_live_ai.py +0 -0
  230. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/live/test_wade_live_gh.py +0 -0
  231. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/test_cli_basics.py +0 -0
  232. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/__init__.py +0 -0
  233. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/conftest.py +0 -0
  234. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/__init__.py +0 -0
  235. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_done_review_hint.py +0 -0
  236. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_knowledge_cli.py +0 -0
  237. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_review_cli.py +0 -0
  238. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_session_subapps.py +0 -0
  239. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_shell_init.py +0 -0
  240. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_task_cli.py +0 -0
  241. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_work_ai_flags.py +0 -0
  242. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_cli/test_work_cli.py +0 -0
  243. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_config/__init__.py +0 -0
  244. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_config/test_loader.py +0 -0
  245. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_config/test_migrations.py +0 -0
  246. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_config/test_migrations_atomic.py +0 -0
  247. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_console.py +0 -0
  248. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_db/__init__.py +0 -0
  249. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_db/test_repository_transactions.py +0 -0
  250. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/__init__.py +0 -0
  251. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/test_branch_slugify.py +0 -0
  252. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/test_detached_worktree.py +0 -0
  253. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/test_pr_base.py +0 -0
  254. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/test_repo.py +0 -0
  255. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/test_scaffold_commit.py +0 -0
  256. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/test_sync_conflicted_files.py +0 -0
  257. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_git/test_worktree_remove.py +0 -0
  258. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_hooks/__init__.py +0 -0
  259. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/__init__.py +0 -0
  260. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_batch.py +0 -0
  261. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_config_models.py +0 -0
  262. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_db.py +0 -0
  263. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_deps_models.py +0 -0
  264. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_deps_partition.py +0 -0
  265. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_review.py +0 -0
  266. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_models/test_task_models.py +0 -0
  267. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_providers/__init__.py +0 -0
  268. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_providers/test_github_pr.py +0 -0
  269. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/__init__.py +0 -0
  270. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_batch_review_service.py +0 -0
  271. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_bootstrap_hook_timeout.py +0 -0
  272. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_delegation_service.py +0 -0
  273. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_implementation_service_resume.py +0 -0
  274. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_knowledge_search.py +0 -0
  275. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_knowledge_service.py +0 -0
  276. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_poll_for_reviews.py +0 -0
  277. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_review_delegation_service.py +0 -0
  278. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_review_service.py +0 -0
  279. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_smart_start.py +0 -0
  280. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_task_service.py +0 -0
  281. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_tracked_managed_files.py +0 -0
  282. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_services/test_worktree_gitignore.py +0 -0
  283. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_skills/test_managed_gitignore.py +0 -0
  284. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/__init__.py +0 -0
  285. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_http.py +0 -0
  286. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_process.py +0 -0
  287. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_prompts.py +0 -0
  288. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_slug.py +0 -0
  289. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_terminal.py +0 -0
  290. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_terminal_launch.py +0 -0
  291. {wade_cli-0.30.2 → wade_cli-0.33.0}/tests/unit/test_utils/test_update_check.py +0 -0
@@ -7,50 +7,49 @@ project:
7
7
  merge_strategy: PR
8
8
  ai:
9
9
  default_tool: claude
10
- default_model: claude-sonnet-4.6
11
- effort: max
10
+ default_model: claude-opus-4.8
11
+ effort: xhigh
12
12
  plan:
13
- tool: claude
14
- model: claude-opus-4.7
15
13
  effort: xhigh
16
14
  yolo: true
17
15
  deps:
18
- tool: copilot
19
- model: claude-sonnet-4.6
16
+ tool: claude
17
+ model: claude-sonnet-5
20
18
  mode: headless
21
- yolo: false
19
+ effort: high
22
20
  review_plan:
23
- tool: copilot
24
- model: claude-sonnet-4.6
21
+ tool: claude
22
+ model: claude-sonnet-5
25
23
  mode: headless
24
+ effort: high
26
25
  enabled: true
27
- yolo: false
28
26
  review_implementation:
29
- tool: copilot
30
- model: claude-sonnet-4.6
27
+ tool: claude
28
+ model: claude-sonnet-5
31
29
  mode: headless
30
+ effort: high
32
31
  enabled: true
33
- yolo: false
34
32
  review_batch:
35
- tool: copilot
36
- model: claude-sonnet-4.6
33
+ tool: claude
34
+ model: claude-sonnet-5
37
35
  mode: interactive
36
+ effort: high
38
37
  enabled: true
39
38
  yolo: true
40
- yolo: true
39
+ yolo: false
41
40
  models:
42
41
  claude:
43
42
  easy:
44
- model: claude-sonnet-4.6
45
- effort: xhigh
46
- medium:
47
- model: claude-sonnet-4.6
43
+ model: claude-haiku-4.5
48
44
  effort: max
49
- complex:
50
- model: claude-opus-4.7
45
+ medium:
46
+ model: claude-sonnet-5
51
47
  effort: high
48
+ complex:
49
+ model: claude-sonnet-5
50
+ effort: xhigh
52
51
  very_complex:
53
- model: claude-opus-4.7
52
+ model: claude-opus-4.8
54
53
  effort: xhigh
55
54
  provider:
56
55
  name: github
@@ -70,7 +70,7 @@ CLI modules are thin dispatch — they parse flags via Typer, then call service
70
70
 
71
71
  ### Key Design Patterns
72
72
 
73
- - **AI Tool Adapters via crossby**: `AbstractAITool` and every tool-specific adapter (Claude, Cursor, Copilot, Gemini, Codex, OpenCode, Antigravity, VS Code) live in the external `crossby` dependency, not this repo. Wade's services import `crossby.ai_tools` and `crossby.models.ai` directly. Adding a new AI tool means contributing to crossby, not wade.
73
+ - **AI Tool Adapters via crossby**: `AbstractAITool` and every tool-specific adapter (Claude, Cursor, Copilot, Codex, OpenCode, Antigravity, Antigravity CLI, VS Code) live in the external `crossby` dependency, not this repo. Wade's services import `crossby.ai_tools` and `crossby.models.ai` directly. Adding a new AI tool means contributing to crossby, not wade.
74
74
  - **Provider Abstraction**: `AbstractTaskProvider` ABC with pluggable backends (GitHub via `gh` CLI, ClickUp via REST API, Markdown via a single central file). Non-GitHub providers compose `GitHubPRDelegateMixin` so PR-review APIs still flow through `gh`.
75
75
  - **Prompts as .md Templates**: All AI prompts live in `templates/prompts/`, not inline strings.
76
76
  - **Synchronous Only**: No asyncio. Process-level parallelism via multiple terminals.
@@ -4,6 +4,48 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Conventional Commits](https://conventionalcommits.org/).
6
6
 
7
+ ## [v0.33.0] — 2026-07-23
8
+
9
+ ### Features
10
+
11
+ - replace Gemini CLI support with Antigravity CLI (bump crossby to 0.10.x) (#336) (c54af6d)
12
+
13
+ ## [v0.32.0] — 2026-07-23
14
+
15
+ ### Features
16
+
17
+ - supersede existing issue when planning splits it into multiple plan files (#331) (2f1fb93)
18
+
19
+ ## [v0.31.3] — 2026-07-22
20
+
21
+ ### Bug Fixes
22
+
23
+ - --mode headless still blocks on TTY AI-selection confirm prompt (#332) (3095ed7)
24
+
25
+ ## [v0.31.2] — 2026-07-17
26
+
27
+ ### Refactoring
28
+
29
+ - convert init_service.py into a package of focused modules (#325) (25d19ef)
30
+
31
+ ## [v0.31.1] — 2026-07-17
32
+
33
+ ### Bug Fixes
34
+
35
+ - remove(stale=True) misclassifies worktree as ACTIVE when provider read fails in CI (regression from #315) (#327) (83edda9)
36
+
37
+ ## [v0.31.0] — 2026-07-17
38
+
39
+ ### Features
40
+
41
+ - modularize implementation_service/core.py into focused submodules (#315) (97c5767)
42
+
43
+ ## [v0.30.3] — 2026-07-17
44
+
45
+ ### Bug Fixes
46
+
47
+ - skip self-upgrade for editable uv-tool installs (#323) (65666ec)
48
+
7
49
  ## [v0.30.2] — 2026-07-16
8
50
 
9
51
  ### Bug Fixes
@@ -7,7 +7,7 @@ Read this at the start of every session. Add new entries via `wade knowledge add
7
7
 
8
8
  ## 6e38b3aa | 2026-03-27 | implementation | Issue #233
9
9
 
10
- After migrating AI tool artifacts off main (#233): The managed gitignore (get_managed_gitignore_patterns in installer.py) now includes .claude/settings.json, .cursor/cli.json, and hook config files (.cursor/hooks.json, .gemini/settings.json, .github/hooks/hooks.json). These were not in the managed block before. The wade repo's own .gitignore also needed manual entries for the session files because the managed block is stale (it's regenerated by wade update, which can't be run from worktrees during implementation). When working in worktrees for the wade repo, always expect untracked session files if the managed gitignore block hasn't been updated recently.
10
+ After migrating AI tool artifacts off main (#233): The managed gitignore (get_managed_gitignore_patterns in installer.py) now includes .claude/settings.json, .cursor/cli.json, and hook config files (.cursor/hooks.json, .github/hooks/hooks.json). These were not in the managed block before. The wade repo's own .gitignore also needed manual entries for the session files because the managed block is stale (it's regenerated by wade update, which can't be run from worktrees during implementation). When working in worktrees for the wade repo, always expect untracked session files if the managed gitignore block hasn't been updated recently.
11
11
 
12
12
  ---
13
13
 
@@ -87,7 +87,7 @@ When editing agent session rules, check BOTH templates/skills/<name>/SKILL.md AN
87
87
 
88
88
  ## 1c18f1d4 | 2026-04-28 | plan | tags: delegation, yolo, headless, ai-tools
89
89
 
90
- wade's headless AI delegation (mode: headless for deps and review_*) should rely on permissions.allowed_commands, not yolo. Why: (1) codex exec auto-defaults approval_policy=never, making yolo args (-a never) redundant. (2) For Claude/Copilot/Gemini/Cursor, headless analytical tasks (read repo, emit output) don't need write permissions, so yolo over-grants. (3) Headless yolo passthrough was added in PR #146 (commit 26771d3) but worked fine before that. The init wizard prompting "Enable YOLO mode for plan review?" on a headless subprocess is confusing UX.
90
+ wade's headless AI delegation (mode: headless for deps and review_*) should rely on permissions.allowed_commands, not yolo. Why: (1) codex exec auto-defaults approval_policy=never, making yolo args (-a never) redundant. (2) For Claude/Copilot/Cursor, headless analytical tasks (read repo, emit output) don't need write permissions, so yolo over-grants. (3) Headless yolo passthrough was added in PR #146 (commit 26771d3) but worked fine before that. The init wizard prompting "Enable YOLO mode for plan review?" on a headless subprocess is confusing UX.
91
91
 
92
92
  ---
93
93
 
@@ -129,3 +129,87 @@ When splitting a wade service module into submodules, moving a function to a new
129
129
  Fix: add a re-exec breadcrumb env var (WADE_SELF_UPGRADE_FROM=<old version>) so a post-re-exec process detects the version did NOT advance and stops with a warning; force a cache-bypassing fresh PyPI check for the explicit self-upgrade path; and skip yanked releases when selecting the latest version.
130
130
 
131
131
  ---
132
+
133
+ ## d65003eb | 2026-07-16 | implementation | tags: testing, mocking | Issue #321
134
+
135
+ Functions imported locally inside a function body (e.g. `from wade.utils.install import detect_install_method` inside `_maybe_self_upgrade()`) must be mock-patched at their *source* module (`wade.utils.install.detect_install_method`), not at the importing module (`wade.services.init_service.detect_install_method`) — the local import re-resolves the name from the source module at call time, so patching the importer's namespace has no effect.
136
+
137
+ ---
138
+
139
+ ## c71d1a70 | 2026-07-16 | implementation | tags: pypi | Issue #321
140
+
141
+ PyPI's JSON API (pypi.org/pypi/<pkg>/json) does not expose a top-level 'yanked' flag on `info` — yanked status lives per-file inside `releases[version]`, where each file dict has a `yanked` bool. `info.version` does not account for yanking, so it can point at a release that pip/uv will refuse to install; select from `releases` and filter out versions where every file is yanked instead of trusting `info.version`.
142
+
143
+ ---
144
+
145
+ ## bcfa4bb3 | 2026-07-17 | implementation | tags: skills, prompts, architecture | Issue #314
146
+
147
+ Session prompt templates (templates/prompts/) are NOT symlinked — they are read directly by service code: plan_service.py and implementation_service/draft_pr.py (build_implementation_prompt, moved there from core.py in the #314 split). Skill files (templates/skills/) ARE symlinked into .claude/skills/. When fixing agent instructions, check BOTH the skill files AND the prompt templates.
148
+
149
+ ---
150
+
151
+ ## 0bad70f3 | 2026-07-17 | implementation | tags: testing, mocking, refactoring | Issue #314
152
+
153
+ When splitting a wade service module, a module-level dependency import must stay module-level (not function-local) if any test patches it via that module (e.g. done.load_config). mock.patch resolves the attribute on the module object; a function-local import re-binds from the source module at call time, so the patch is silently ignored. Also: a moved helper imported into several modules is patched in the CALLER's namespace (e.g. find_worktree_path is patched at done./cleanup./core. depending on which function is under test), not where it is defined (_shared).
154
+
155
+ ---
156
+
157
+ ## b7ff010c | 2026-07-17 | implementation | tags: refactoring, architecture, imports | Issue #317
158
+
159
+ When splitting a service module into a package, a constant/helper shared by a leaf submodule and an upper one must live in the LEAF, not the upper module — the upper already imports from the leaf, so hosting it upstream creates an import cycle. E.g. init_service `_COMMAND_OVERRIDE_NAMES` belongs in config_io (a leaf), not prompts_setup, since prompts_setup imports `_resolve_models` from config_io.
160
+
161
+ ---
162
+
163
+ ## 129ab9bd | 2026-07-17 | plan | tags: testing, providers, error-handling | Issue #326
164
+
165
+ GitHubProvider.read_task_or_none() raises RuntimeError (not silently returns None) on any gh CLI failure that isn't a genuine 'not found' match — e.g. missing GH_TOKEN, auth, network, rate-limit. Callers relying on it as a fail-safe (e.g. classify_staleness's task_lookup_failed) must catch exceptions around the call, not just check for None.
166
+
167
+ ---
168
+
169
+ ## 3045ea00 | 2026-07-17 | plan | tags: testing, mocking, ci | Issue #326
170
+
171
+ Tests that call remove(stale=True)/_remove_stale() or list_sessions() in cleanup.py must mock wade.services.implementation_service.cleanup.get_provider (not just cleanup.load_config), or they instantiate a real GitHubProvider and shell out to gh — passing locally with authenticated gh but failing in CI with no GH_TOKEN. See TestListSessions.test_gracefully_handles_issue_lookup_failures for the mocking pattern.
172
+
173
+ ---
174
+
175
+ ## 702b274f | 2026-07-21 | plan | tags: prompts, cli, ux
176
+
177
+ wade/ui/prompts.py helpers such as confirm, input_prompt, select, and menu return their configured default when stdin is not a TTY; multi_select returns all items. When adding an interactive prompt in a service/CLI path, do NOT write your own non-interactive detection — just pass a sensible default and rely on this. The idiom for an auto-proceed-under-yolo confirmation is: proceed = yolo or prompts.confirm(msg, default=True).
178
+
179
+ ---
180
+
181
+ ## c7a450ff | 2026-07-21 | implementation | tags: testing, python | Issue #330
182
+
183
+ Python's parenthesized with-statement (with (a, b, c):) does not support unpacking a list of context managers via *patches — it's a syntax error, not a runtime one. To share a list of mock.patch(...) objects across multiple tests, use contextlib.ExitStack() and call stack.enter_context(p) for each, capturing return values for assertions.
184
+
185
+ ---
186
+
187
+ ## b6ca74e5 | 2026-07-23 | plan | tags: git, gh, pr-merge | Issue #333
188
+
189
+ gh pr merge <n> --squash --delete-branch (wrapped by git_pr.merge_pr, run with cwd=repo_root) performs the remote squash-merge FIRST, then does local --delete-branch bookkeeping which resolves the cwd repo's current branch. If that clone has a detached HEAD, gh aborts with "could not determine current branch: not on any branch" AFTER the PR is already merged on GitHub — a "merged-on-GitHub-but-reported-failed" inconsistency. Any guard on repo_root HEAD state (e.g. re-attaching it) MUST run BEFORE calling merge_pr, so a fail-fast path avoids the irreversible remote merge. _merge_pr in lifecycle.py is shared by review_service.py and smart_start.py.
190
+
191
+ ---
192
+
193
+ ## 9f5e5e2c | 2026-07-23 | plan | tags: crossby, ai-tools, permission-mode
194
+
195
+ crossby exposes an autonomy/permission axis orthogonal to WADE's DelegationMode (prompt/interactive/headless): pass yolo/auto/accept_edits booleans to AbstractAITool.launch()/build_launch_command(). crossby owns the precedence ladder yolo > auto > accept_edits > default > plan and does capability-aware downgrades with warnings in _autonomy_launch_args (ai_tools/base.py) — WADE forwards intent, it must not re-implement gating. auto (classifier-mediated) is Claude-only among CLIs and downgrades to accept-edits elsewhere.
196
+
197
+ ---
198
+
199
+ ## 38aa4f95 | 2026-07-23 | plan | tags: crossby, ai-tools, antigravity
200
+
201
+ crossby has two separate Antigravity adapters, easy to confuse: AIToolID.ANTIGRAVITY (GUI IDE, binary "antigravity", non-blocking/detached, no headless or yolo) and AIToolID.ANTIGRAVITY_CLI = "antigravity-cli" (terminal agent, binary "agy", blocking, supports headless --print, effort, yolo, and plan/accept-edits via --mode). The CLI one is the Gemini-CLI replacement and flows through WADE's standard terminal launch path.
202
+
203
+ ---
204
+
205
+ ## 669d0bb8 | 2026-07-23 | implementation | tags: migration, symlinks, gotcha | Issue #335
206
+
207
+ wade cross-tool skill alias dirs (.gemini/skills, .github/skills, .agents/skills, .cursor/skills) are SYMLINKS to .claude/skills, not real dirs. Cleanup/migration code must remove them via is_symlink()/unlink(), never is_dir()/rmdir(): Path.is_dir() returns False on a dangling symlink (the .claude/skills target may already be pruned earlier in the same wade update run), and os.rmdir() on a live symlink-to-dir raises NotADirectoryError. Only rmdir the real .gemini sub-dirs (hooks, policies) and guard with 'not is_symlink() and not any(iterdir())'.
208
+
209
+ ---
210
+
211
+ ## 9b40e000 | 2026-07-23 | implementation | tags: ai-tools, antigravity, testing | Issue #335
212
+
213
+ crossby's antigravity-cli adapter is permissive where the removed gemini adapter was strict: is_model_compatible() returns True for ANY model (including claude-* models), and capabilities().supports_effort is True. Consequences for wade tests: (1) a tool/model incompatibility test (resolve_model -> None) cannot use antigravity-cli — use codex, which rejects claude models; (2) capability-gated interactive wizard tests (_prompt_command_overrides) gain an extra effort prompt when the override tool is antigravity-cli, shifting mock_select index sequences vs the old gemini flow (gemini had supports_effort=False).
214
+
215
+ ---
@@ -0,0 +1,62 @@
1
+ 0bad70f3:
2
+ down: 0
3
+ stale: 0
4
+ up: 3
5
+ 164c355e:
6
+ down: 0
7
+ stale: 0
8
+ up: 1
9
+ 1c18f1d4:
10
+ down: 0
11
+ stale: 0
12
+ up: 1
13
+ 2b4d5319:
14
+ down: 0
15
+ stale: 0
16
+ up: 2
17
+ 3045ea00:
18
+ down: 0
19
+ stale: 0
20
+ up: 1
21
+ 3463fbd4:
22
+ down: 0
23
+ stale: 0
24
+ up: 2
25
+ 4863036c:
26
+ down: 0
27
+ stale: 0
28
+ up: 4
29
+ 54326c98:
30
+ down: 1
31
+ stale: 0
32
+ up: 0
33
+ a24d65b0:
34
+ down: 0
35
+ stale: 0
36
+ superseded_by: c71d1a70
37
+ up: 1
38
+ b61e247e:
39
+ down: 0
40
+ stale: 0
41
+ superseded_by: bcfa4bb3
42
+ up: 4
43
+ b6a3a637:
44
+ down: 0
45
+ stale: 0
46
+ up: 1
47
+ cc91cd11:
48
+ down: 0
49
+ stale: 0
50
+ up: 3
51
+ cedeaad0:
52
+ down: 1
53
+ stale: 0
54
+ up: 1
55
+ d65003eb:
56
+ down: 0
57
+ stale: 0
58
+ up: 2
59
+ e2a49ac1:
60
+ down: 0
61
+ stale: 0
62
+ up: 3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wade-cli
3
- Version: 0.30.2
3
+ Version: 0.33.0
4
4
  Summary: WADE — Workflow for AI-Driven Engineering
5
5
  Project-URL: Homepage, https://github.com/ivanviragine/wade
6
6
  Project-URL: Repository, https://github.com/ivanviragine/wade
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
17
  Classifier: Topic :: Software Development :: Version Control :: Git
18
18
  Requires-Python: >=3.11
19
- Requires-Dist: crossby<0.4.0,>=0.3.1
19
+ Requires-Dist: crossby<0.11.0,>=0.10.1
20
20
  Requires-Dist: httpx<1.0,>=0.27
21
21
  Requires-Dist: pydantic-settings>=2.0
22
22
  Requires-Dist: pydantic>=2.0
@@ -45,7 +45,7 @@ Description-Content-Type: text/markdown
45
45
 
46
46
  *Every Wade does the dirty work so the heroes don't have to.*
47
47
 
48
- Branches, worktrees, context loading, model selection, PR creation — all the workflow friction that surrounds AI coding sessions. WADE eliminates it. Works with `Claude Code`, `Copilot`, `Gemini`, `Codex`, and more. Run `wade init` once per project, then just point it at a GitHub *(more to come!)* issue number.
48
+ Branches, worktrees, context loading, model selection, PR creation — all the workflow friction that surrounds AI coding sessions. WADE eliminates it. Works with `Claude Code`, `Copilot`, `Antigravity CLI`, `Codex`, and more. Run `wade init` once per project, then just point it at a GitHub *(more to come!)* issue number.
49
49
 
50
50
  ## See It in Action
51
51
 
@@ -181,6 +181,15 @@ Short aliases: `wade p` (plan), `wade i <N>` (implement), `wade r <N>` (review p
181
181
 
182
182
  Most workflow commands accept `--ai <tool>`, `--model <model>`, `--effort <level>`, and `--yolo` to override configured defaults. `implement` also supports `--detach` (new terminal tab) and `--cd` (print worktree path only).
183
183
 
184
+ `wade plan --issue <N>` re-plans an existing issue. If the session produces a
185
+ single plan file, it's attached to `#N` and the issue stays open. If the
186
+ session decides the work should be split into several independent pieces
187
+ (2+ plan files), `#N` is **superseded**: a new issue + draft PR is created
188
+ per plan file, a comment and a `> **Superseded by ...**` banner are added to
189
+ `#N`, and it's closed as *not planned* (confirmed via prompt unless
190
+ `--yolo`/non-interactive). If any plan file fails to become an issue, `#N` is
191
+ left open with a warning instead of superseding on a partial split.
192
+
184
193
  ## Supported AI Tools
185
194
 
186
195
  Adapters, model/effort resolution, and per-tool config (allowlists, hooks) are powered by [`crossby`](https://github.com/ivanviragine/crossby), WADE's AI-tool-integration dependency.
@@ -190,11 +199,14 @@ Adapters, model/effort resolution, and per-tool config (allowlists, hooks) are p
190
199
  | [Claude Code](https://claude.com/product/claude-code) | `claude` |
191
200
  | [Cursor](https://www.cursor.com/) | `cursor` |
192
201
  | [GitHub Copilot](https://github.com/features/copilot/cli) | `copilot` |
193
- | [Google Gemini](https://geminicli.com/) | `gemini` |
194
202
  | [OpenAI Codex](https://developers.openai.com/codex/cli/) | `codex` |
195
203
  | [OpenCode](https://opencode.ai/) | `opencode` |
196
204
  | [VS Code](https://github.com/features/copilot/ai-code-editor) | `code` |
197
205
  | [Antigravity](https://antigravity.google/) | `antigravity` |
206
+ | [Antigravity CLI](https://antigravity.google/) | `agy` |
207
+
208
+ > In `.wade.yml`, refer to a tool by its **config ID**, which matches the binary
209
+ > above except for VS Code (`vscode`) and Antigravity CLI (`antigravity-cli`).
198
210
 
199
211
  ## Task Providers
200
212
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  *Every Wade does the dirty work so the heroes don't have to.*
10
10
 
11
- Branches, worktrees, context loading, model selection, PR creation — all the workflow friction that surrounds AI coding sessions. WADE eliminates it. Works with `Claude Code`, `Copilot`, `Gemini`, `Codex`, and more. Run `wade init` once per project, then just point it at a GitHub *(more to come!)* issue number.
11
+ Branches, worktrees, context loading, model selection, PR creation — all the workflow friction that surrounds AI coding sessions. WADE eliminates it. Works with `Claude Code`, `Copilot`, `Antigravity CLI`, `Codex`, and more. Run `wade init` once per project, then just point it at a GitHub *(more to come!)* issue number.
12
12
 
13
13
  ## See It in Action
14
14
 
@@ -144,6 +144,15 @@ Short aliases: `wade p` (plan), `wade i <N>` (implement), `wade r <N>` (review p
144
144
 
145
145
  Most workflow commands accept `--ai <tool>`, `--model <model>`, `--effort <level>`, and `--yolo` to override configured defaults. `implement` also supports `--detach` (new terminal tab) and `--cd` (print worktree path only).
146
146
 
147
+ `wade plan --issue <N>` re-plans an existing issue. If the session produces a
148
+ single plan file, it's attached to `#N` and the issue stays open. If the
149
+ session decides the work should be split into several independent pieces
150
+ (2+ plan files), `#N` is **superseded**: a new issue + draft PR is created
151
+ per plan file, a comment and a `> **Superseded by ...**` banner are added to
152
+ `#N`, and it's closed as *not planned* (confirmed via prompt unless
153
+ `--yolo`/non-interactive). If any plan file fails to become an issue, `#N` is
154
+ left open with a warning instead of superseding on a partial split.
155
+
147
156
  ## Supported AI Tools
148
157
 
149
158
  Adapters, model/effort resolution, and per-tool config (allowlists, hooks) are powered by [`crossby`](https://github.com/ivanviragine/crossby), WADE's AI-tool-integration dependency.
@@ -153,11 +162,14 @@ Adapters, model/effort resolution, and per-tool config (allowlists, hooks) are p
153
162
  | [Claude Code](https://claude.com/product/claude-code) | `claude` |
154
163
  | [Cursor](https://www.cursor.com/) | `cursor` |
155
164
  | [GitHub Copilot](https://github.com/features/copilot/cli) | `copilot` |
156
- | [Google Gemini](https://geminicli.com/) | `gemini` |
157
165
  | [OpenAI Codex](https://developers.openai.com/codex/cli/) | `codex` |
158
166
  | [OpenCode](https://opencode.ai/) | `opencode` |
159
167
  | [VS Code](https://github.com/features/copilot/ai-code-editor) | `code` |
160
168
  | [Antigravity](https://antigravity.google/) | `antigravity` |
169
+ | [Antigravity CLI](https://antigravity.google/) | `agy` |
170
+
171
+ > In `.wade.yml`, refer to a tool by its **config ID**, which matches the binary
172
+ > above except for VS Code (`vscode`) and Antigravity CLI (`antigravity-cli`).
161
173
 
162
174
  ## Task Providers
163
175
 
@@ -130,11 +130,11 @@ AI tool adapters, model/effort resolution primitives, the model registry, and pe
130
130
 
131
131
  | What | Lives in crossby | Used from wade via |
132
132
  |------|-------------------|---------------------|
133
- | `AbstractAITool` + adapters (Claude, Cursor, Copilot, Gemini, Codex, OpenCode, Antigravity, VS Code) | `crossby.ai_tools` | `services/ai_resolution.py`, `delegation_service.py`, `review_service.py`, `plan_service.py`, `prompt_delivery.py` |
133
+ | `AbstractAITool` + adapters (Claude, Cursor, Copilot, Codex, OpenCode, Antigravity, Antigravity CLI, VS Code) | `crossby.ai_tools` | `services/ai_resolution.py`, `delegation_service.py`, `review_service.py`, `plan_service.py`, `prompt_delivery.py` |
134
134
  | `AIToolID`, `AIModel`, `ModelTier`, `TokenUsage`, `AIToolCapabilities`, `EffortLevel` | `crossby.models.ai` | re-exported from `wade.models` |
135
135
  | Model registry (probed from CLIs, was `data/models.json`) | `crossby.data.get_models_for_tool()` | `ai_resolution.py`, `init_service.py` |
136
136
  | Per-tool default model tiers (was `config/defaults.py`) | `crossby.config.defaults.get_defaults()` | `init_service.py` |
137
- | Claude/Cursor allowlist management, Cursor/Copilot/Gemini hook config (was `config/*_allowlist.py`, `config/*_hooks.py`) | `crossby.config.*`, `crossby.sync.permissions` | `implementation_service/bootstrap.py`, `init_service.py` |
137
+ | Claude/Cursor allowlist management, Cursor/Copilot hook config (was `config/*_allowlist.py`, `config/*_hooks.py`) | `crossby.config.*`, `crossby.sync.permissions` | `implementation_service/bootstrap.py`, `init_service.py` |
138
138
 
139
139
  Wade still owns a thin `services/ai_resolution.py` rather than delegating outright: wade's `ProjectConfig.ai` uses named per-command fields (e.g. `ai.plan`), while crossby's own config expects a `commands` dict — the two shapes aren't interchangeable yet. See that module's docstring for the up-to-date status.
140
140
 
@@ -218,9 +218,8 @@ knowledge:
218
218
  6. Reload config + backfill probed models
219
219
  7. Refresh skill files
220
220
  8. Configure AI tool allowlists (Claude via `claude_allowlist.py`, Cursor via `cursor_allowlist.py`)
221
- 9. Configure Gemini experimental settings (if applicable)
222
- 10. Refresh .gitignore + AGENTS.md pointer
223
- 11. Rebuild manifest with version
221
+ 9. Refresh .gitignore + AGENTS.md pointer
222
+ 10. Rebuild manifest with version
224
223
 
225
224
  **Self-upgrade mechanism**: `utils/install.py:detect_install_method()` inspects `sys.executable` to determine how wade was installed (`uv-tool`, `pipx`, `brew`, or `editable`). On `wade update`, `self_upgrade()` runs the appropriate package manager command (e.g. `uv tool upgrade wade`), then `re_exec()` replaces the current process via `os.execv()` so the new code is loaded. Editable installs skip this naturally. Pass `--skip-self-upgrade` to bypass.
226
225
 
@@ -35,13 +35,12 @@ When `wade init` runs on this repo (self-init mode), the installer creates symli
35
35
 
36
36
  .github/skills/ -> (same targets, separate symlinks)
37
37
  .agents/skills/ -> (same targets, separate symlinks)
38
- .gemini/skills/ -> (same targets, separate symlinks)
39
38
  .cursor/skills/ -> (same targets, separate symlinks)
40
39
  ```
41
40
 
42
41
  Note: These symlinks are created by `wade init` with `is_self_init=True`. If they don't exist, run `wade init` in this repo to create them.
43
42
 
44
- **Always edit `templates/skills/<name>/SKILL.md`** — never edit files inside `.claude/skills/`, `.github/skills/`, `.agents/skills/`, `.gemini/skills/`, or `.cursor/skills/` directly. In this repo those are symlinks back to templates; in inited projects they are copies that would be overwritten by `wade update`.
43
+ **Always edit `templates/skills/<name>/SKILL.md`** — never edit files inside `.claude/skills/`, `.github/skills/`, `.agents/skills/`, or `.cursor/skills/` directly. In this repo those are symlinks back to templates; in inited projects they are copies that would be overwritten by `wade update`.
45
44
 
46
45
  In inited projects (normal init), `wade init` copies skill files (not symlinks), so agents in those projects read standalone files that don't change unless `wade update` is run.
47
46
 
@@ -76,7 +75,7 @@ The installer expands these placeholders when copying skill files to a project.
76
75
 
77
76
  > **Scope: inited projects.** The skill templates in `templates/skills/` are installed into inited projects by `wade init`. They are *not* guidance for developing wade itself — they teach AI agents in target projects how to use the wade workflow. When you are developing wade, treat these files as **output artifacts** you are authoring, not as rules you follow.
78
77
 
79
- Skill templates are Markdown files installed to an inited project's `.claude/skills/` by `wade init`, with symlinks from `.github/skills/`, `.agents/skills/`, `.gemini/skills/`, and `.cursor/skills/` for cross-tool discovery. They teach AI agents the wade workflow via phase-specific session skills and on-demand task skills.
78
+ Skill templates are Markdown files installed to an inited project's `.claude/skills/` by `wade init`, with symlinks from `.github/skills/`, `.agents/skills/`, and `.cursor/skills/` for cross-tool discovery. They teach AI agents the wade workflow via phase-specific session skills and on-demand task skills.
80
79
 
81
80
  ### Phase-Specific Skill Architecture (for inited projects)
82
81
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wade-cli"
7
- version = "0.30.2"
7
+ version = "0.33.0"
8
8
  description = "WADE — Workflow for AI-Driven Engineering"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -24,7 +24,7 @@ classifiers = [
24
24
  "Topic :: Software Development :: Version Control :: Git",
25
25
  ]
26
26
  dependencies = [
27
- "crossby>=0.3.1,<0.4.0",
27
+ "crossby>=0.10.1,<0.11.0",
28
28
  "typer>=0.12,<0.26",
29
29
  "pydantic>=2.0",
30
30
  "pydantic-settings>=2.0",
@@ -1,3 +1,3 @@
1
1
  """WADE (Workflow for AI-Driven Engineering) — AI-agent-driven git workflow management CLI."""
2
2
 
3
- __version__ = "0.30.2"
3
+ __version__ = "0.33.0"
@@ -2,7 +2,7 @@
2
2
  """Plan-session file-write guard for AI tools.
3
3
 
4
4
  Standalone script (no wade imports) that intercepts pre-tool-use hooks from
5
- Claude Code, Cursor, Copilot, and Gemini CLI. Blocks writes to files that
5
+ Claude Code, Cursor, and Copilot. Blocks writes to files that
6
6
  are NOT plan artifacts.
7
7
 
8
8
  Allowed basenames / patterns:
@@ -73,9 +73,8 @@ def _extract_file_path(data: dict[str, object]) -> str | None:
73
73
  - Claude Code: tool_input.file_path or tool_input.command (for Write/Edit)
74
74
  - Cursor: tool_input.filePath or tool_input.file_path
75
75
  - Copilot: toolArgs (JSON string) with file/path/filePath keys
76
- - Gemini: tool_input.file_path or tool_input.path
77
76
  """
78
- # Claude Code / Cursor / Gemini: tool_input dict
77
+ # Claude Code / Cursor: tool_input dict
79
78
  tool_input = data.get("tool_input") or data.get("toolInput") or {}
80
79
  if isinstance(tool_input, dict):
81
80
  for key in ("file_path", "filePath", "path"):
@@ -130,8 +129,8 @@ def _deny(file_path: str) -> None:
130
129
  )
131
130
  # stderr for human-readable output
132
131
  print(msg, file=sys.stderr)
133
- # stdout JSON — Claude Code strict schema: only hookSpecificOutput at root
134
- # Gemini uses exit code 2 as its block signal regardless of JSON
132
+ # stdout JSON — Claude Code strict schema: only hookSpecificOutput at root.
133
+ # Exit code 2 is the block signal regardless of whether JSON is consumed.
135
134
  result = {
136
135
  "hookSpecificOutput": {
137
136
  "hookEventName": "PreToolUse",
@@ -2,7 +2,7 @@
2
2
  """Worktree file-write guard for AI tools.
3
3
 
4
4
  Standalone script (no wade imports) that intercepts pre-tool-use hooks from
5
- Claude Code, Cursor, Copilot, and Gemini CLI. Blocks writes to files outside
5
+ Claude Code, Cursor, and Copilot. Blocks writes to files outside
6
6
  the worktree root.
7
7
 
8
8
  The worktree root is derived from this script's install location:
@@ -62,9 +62,8 @@ def _extract_file_path(data: dict[str, object]) -> str | None:
62
62
  - Claude Code: tool_input.file_path or tool_input.path
63
63
  - Cursor: tool_input.filePath or tool_input.file_path
64
64
  - Copilot: toolArgs (JSON string) with file/path/filePath keys
65
- - Gemini: tool_input.file_path or tool_input.path
66
65
  """
67
- # Claude Code / Cursor / Gemini: tool_input dict
66
+ # Claude Code / Cursor: tool_input dict
68
67
  tool_input = data.get("tool_input") or data.get("toolInput") or {}
69
68
  if isinstance(tool_input, dict):
70
69
  for key in ("file_path", "filePath", "path"):
@@ -117,8 +116,8 @@ def _deny(file_path: str, worktree_root: Path) -> None:
117
116
  )
118
117
  # stderr for human-readable output
119
118
  print(msg, file=sys.stderr)
120
- # stdout JSON — Claude Code strict schema: only hookSpecificOutput at root
121
- # Gemini uses exit code 2 as its block signal regardless of JSON
119
+ # stdout JSON — Claude Code strict schema: only hookSpecificOutput at root.
120
+ # Exit code 2 is the block signal regardless of whether JSON is consumed.
122
121
  result = {
123
122
  "hookSpecificOutput": {
124
123
  "hookEventName": "PreToolUse",
@@ -25,7 +25,7 @@ class DelegationRequest(BaseModel):
25
25
  model: str | None = None
26
26
  effort: str | None = None
27
27
  cwd: Path | None = None
28
- timeout: int = 120
28
+ timeout: int = 300
29
29
  output_file: Path | None = None
30
30
  trusted_dirs: list[str] = Field(default_factory=list)
31
31
  allowed_commands: list[str] = Field(default_factory=list)
@@ -27,6 +27,18 @@ class TaskState(StrEnum):
27
27
  CLOSED = "closed"
28
28
 
29
29
 
30
+ class CloseReason(StrEnum):
31
+ """Typed reason for closing a task — maps to provider-specific close semantics.
32
+
33
+ GitHub renders ``NOT_PLANNED`` as a grey "closed as not planned" state,
34
+ distinct from the default "closed as completed". Providers without a
35
+ close-reason concept accept and ignore it.
36
+ """
37
+
38
+ COMPLETED = "completed"
39
+ NOT_PLANNED = "not planned"
40
+
41
+
30
42
  class LabelType(StrEnum):
31
43
  """Categories of labels managed by WADE."""
32
44
 
@@ -10,7 +10,7 @@ from abc import ABC, abstractmethod
10
10
 
11
11
  from wade.models.config import ProviderConfig
12
12
  from wade.models.review import PRComment, PRReviewStatus, ReviewThread
13
- from wade.models.task import Label, Task, TaskState
13
+ from wade.models.task import CloseReason, Label, Task, TaskState
14
14
 
15
15
 
16
16
  class AbstractTaskProvider(ABC):
@@ -85,8 +85,11 @@ class AbstractTaskProvider(ABC):
85
85
  """Update task title and/or body."""
86
86
 
87
87
  @abstractmethod
88
- def close_task(self, task_id: str) -> Task:
89
- """Close a task."""
88
+ def close_task(self, task_id: str, reason: CloseReason | None = None) -> Task:
89
+ """Close a task, optionally with a typed reason (e.g. GitHub's "not planned").
90
+
91
+ Providers without a close-reason concept accept and ignore ``reason``.
92
+ """
90
93
 
91
94
  @abstractmethod
92
95
  def comment_on_task(self, task_id: str, body: str) -> None:
@@ -14,6 +14,7 @@ import structlog
14
14
 
15
15
  from wade.models.config import ProviderConfig
16
16
  from wade.models.task import (
17
+ CloseReason,
17
18
  Label,
18
19
  Task,
19
20
  TaskState,
@@ -219,8 +220,12 @@ class ClickUpProvider(GitHubPRDelegateMixin, AbstractTaskProvider):
219
220
 
220
221
  return self.read_task(task_id)
221
222
 
222
- def close_task(self, task_id: str) -> Task:
223
- """Close a task by setting its status to 'closed'."""
223
+ def close_task(self, task_id: str, reason: CloseReason | None = None) -> Task:
224
+ """Close a task by setting its status to 'closed'.
225
+
226
+ ClickUp has no close-reason concept — ``reason`` is accepted for
227
+ interface parity with other providers and ignored.
228
+ """
224
229
  self._client.put(
225
230
  f"/api/v2/task/{task_id}",
226
231
  json={"status": "closed"},
@@ -31,6 +31,7 @@ from wade.models.review import (
31
31
  filter_unresolved_threads,
32
32
  )
33
33
  from wade.models.task import (
34
+ CloseReason,
34
35
  Label,
35
36
  Task,
36
37
  TaskState,
@@ -269,10 +270,13 @@ class GitHubProvider(AbstractTaskProvider):
269
270
 
270
271
  return self.read_task(task_id)
271
272
 
272
- def close_task(self, task_id: str) -> Task:
273
- """Close an issue."""
274
- run(["gh", "issue", "close", task_id], check=True, retries=3)
275
- logger.info("github.issue_closed", number=task_id)
273
+ def close_task(self, task_id: str, reason: CloseReason | None = None) -> Task:
274
+ """Close an issue, optionally with a reason (e.g. "not planned")."""
275
+ cmd = ["gh", "issue", "close", task_id]
276
+ if reason is not None:
277
+ cmd.extend(["--reason", reason.value])
278
+ run(cmd, check=True, retries=3)
279
+ logger.info("github.issue_closed", number=task_id, reason=reason.value if reason else None)
276
280
  return self.read_task(task_id)
277
281
 
278
282
  def comment_on_task(self, task_id: str, body: str) -> None: