lemming-cli 0.4.6__tar.gz → 0.5.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 (194) hide show
  1. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/PKG-INFO +53 -25
  2. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/README.md +51 -24
  3. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/SKILL.md +72 -5
  4. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/pyproject.toml +2 -1
  5. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/exec_cli.py +22 -5
  6. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/exec_cli_test.py +248 -1
  7. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/main_test.py +1 -1
  8. lemming_cli-0.5.0/src/lemming/cli/skill.py +7 -0
  9. lemming_cli-0.5.0/src/lemming/cli/skill_test.py +47 -0
  10. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/orchestrator.py +101 -46
  11. lemming_cli-0.5.0/src/lemming/prompts/hooks/60-testing.md +65 -0
  12. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/prompts/hooks/90-roadmap.md +4 -3
  13. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/uv.lock +19 -1
  14. lemming_cli-0.4.6/src/lemming/cli/skill.py +0 -344
  15. lemming_cli-0.4.6/src/lemming/cli/skill_test.py +0 -151
  16. lemming_cli-0.4.6/src/lemming/prompts/hooks/60-testing.md +0 -48
  17. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/.dockerignore +0 -0
  18. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/.github/workflows/ci.yml +0 -0
  19. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/.github/workflows/publish.yml +0 -0
  20. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/.github/workflows/screenshots.yml +0 -0
  21. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/.gitignore +0 -0
  22. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/.prettierignore +0 -0
  23. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/.prettierrc +0 -0
  24. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/Dockerfile +0 -0
  25. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/LICENSE +0 -0
  26. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/biome.json +0 -0
  27. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/docker-compose.yml +0 -0
  28. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/docs/EVALS.md +0 -0
  29. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/docs/HOOKS.md +0 -0
  30. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/docs/screenshots/dashboard-desktop.png +0 -0
  31. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/docs/screenshots/dashboard-mobile.png +0 -0
  32. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/docs/screenshots/task-log-desktop.png +0 -0
  33. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/docs/screenshots/task-log-mobile.png +0 -0
  34. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/package-lock.json +0 -0
  35. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/package.json +0 -0
  36. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/playwright.config.js +0 -0
  37. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/pyrefly.toml +0 -0
  38. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/__init__.py +0 -0
  39. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/__init__.py +0 -0
  40. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/auth.py +0 -0
  41. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/auth_test.py +0 -0
  42. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/config.py +0 -0
  43. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/config_test.py +0 -0
  44. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/conftest.py +0 -0
  45. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/context.py +0 -0
  46. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/context_test.py +0 -0
  47. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/directories.py +0 -0
  48. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/directories_test.py +0 -0
  49. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/files.py +0 -0
  50. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/files_test.py +0 -0
  51. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/hooks.py +0 -0
  52. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/hooks_test.py +0 -0
  53. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/logging.py +0 -0
  54. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/logging_test.py +0 -0
  55. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/loop.py +0 -0
  56. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/loop_test.py +0 -0
  57. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/main.py +0 -0
  58. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/main_test.py +0 -0
  59. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/tasks.py +0 -0
  60. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api/tasks_test.py +0 -0
  61. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/api_test.py +0 -0
  62. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/brief_test.py +0 -0
  63. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/__init__.py +0 -0
  64. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/brief.py +0 -0
  65. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/config.py +0 -0
  66. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/config_test.py +0 -0
  67. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/goal.py +0 -0
  68. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/goal_test.py +0 -0
  69. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/hooks.py +0 -0
  70. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/hooks_test.py +0 -0
  71. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/machine_output_test.py +0 -0
  72. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/main.py +0 -0
  73. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/operations.py +0 -0
  74. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/operations_test.py +0 -0
  75. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/progress.py +0 -0
  76. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/progress_test.py +0 -0
  77. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/readability_cli.py +0 -0
  78. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/readability_cli_test.py +0 -0
  79. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/stop_test.py +0 -0
  80. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/tasks.py +0 -0
  81. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli/tasks_test.py +0 -0
  82. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/cli_test.py +0 -0
  83. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/conftest.py +0 -0
  84. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/__init__.py +0 -0
  85. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/__main__.py +0 -0
  86. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/cli.py +0 -0
  87. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/cli_test.py +0 -0
  88. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/container.py +0 -0
  89. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/container_test.py +0 -0
  90. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/fixtures.py +0 -0
  91. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/fixtures_test.py +0 -0
  92. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/harness.py +0 -0
  93. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/harness_test.py +0 -0
  94. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/metrics.py +0 -0
  95. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/metrics_test.py +0 -0
  96. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/base/README.md +0 -0
  97. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/base/calc/__init__.py +0 -0
  98. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/base/calc/legacy.py +0 -0
  99. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/base/calc/ops.py +0 -0
  100. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/base/calc/ops_test.py +0 -0
  101. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/clean/calc/ops.py +0 -0
  102. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/clean/calc/ops_test.py +0 -0
  103. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/dead-code/calc/ops.py +0 -0
  104. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/dead-code/calc/ops_test.py +0 -0
  105. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/duplicated-behavior/calc/ops.py +0 -0
  106. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/duplicated-behavior/calc/ops_test.py +0 -0
  107. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/false-reuse/calc/limits.py +0 -0
  108. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/false-reuse/calc/limits_test.py +0 -0
  109. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/lint-debt/calc/ops.py +0 -0
  110. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/readability/lint-debt/calc/ops_test.py +0 -0
  111. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/add-only/README.md +0 -0
  112. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/add-only/calc/__init__.py +0 -0
  113. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/add-only/calc/ops.py +0 -0
  114. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/add-only/calc/ops_test.py +0 -0
  115. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/buggy-subtract/README.md +0 -0
  116. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/__init__.py +0 -0
  117. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/ops.py +0 -0
  118. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/ops_test.py +0 -0
  119. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/full-ops/README.md +0 -0
  120. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/full-ops/calc/__init__.py +0 -0
  121. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/full-ops/calc/ops.py +0 -0
  122. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/full-ops/calc/ops_test.py +0 -0
  123. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/unwired-cli/README.md +0 -0
  124. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/__init__.py +0 -0
  125. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/cli.py +0 -0
  126. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/cli_test.py +0 -0
  127. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/ops.py +0 -0
  128. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/ops_test.py +0 -0
  129. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/task/hidden/stats/summary_hidden_test.py +0 -0
  130. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/task/summary/README.md +0 -0
  131. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/task/summary/stats/__init__.py +0 -0
  132. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/task/summary/stats/summary.py +0 -0
  133. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/projects/task/summary/stats/summary_test.py +0 -0
  134. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/readability.py +0 -0
  135. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/readability_test.py +0 -0
  136. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/report.py +0 -0
  137. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/report_test.py +0 -0
  138. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/roadmap.py +0 -0
  139. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/roadmap_test.py +0 -0
  140. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/scenarios.py +0 -0
  141. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/scenarios_test.py +0 -0
  142. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/suites.py +0 -0
  143. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/task.py +0 -0
  144. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/task_test.py +0 -0
  145. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/trial.py +0 -0
  146. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/evals/trial_test.py +0 -0
  147. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/hooks.py +0 -0
  148. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/hooks_test.py +0 -0
  149. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/integration_test.py +0 -0
  150. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/main.py +0 -0
  151. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/main_test.py +0 -0
  152. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/models.py +0 -0
  153. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/models_test.py +0 -0
  154. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/orchestrator_test.py +0 -0
  155. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/paths.py +0 -0
  156. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/paths_test.py +0 -0
  157. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/persistence.py +0 -0
  158. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/persistence_test.py +0 -0
  159. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/prompts/hooks/50-readability.md +0 -0
  160. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/prompts/hooks/70-ux.md +0 -0
  161. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/prompts/taskrunner.md +0 -0
  162. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/prompts.py +0 -0
  163. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/prompts_test.py +0 -0
  164. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/providers.py +0 -0
  165. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/providers_test.py +0 -0
  166. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/runner.py +0 -0
  167. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/runner_test.py +0 -0
  168. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/scope.py +0 -0
  169. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/scope_test.py +0 -0
  170. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/shutdown.py +0 -0
  171. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/shutdown_integration_test.py +0 -0
  172. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/shutdown_test.py +0 -0
  173. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/__init__.py +0 -0
  174. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/lifecycle.py +0 -0
  175. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/lifecycle_test.py +0 -0
  176. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/limits.py +0 -0
  177. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/operations.py +0 -0
  178. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/operations_test.py +0 -0
  179. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/progress.py +0 -0
  180. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/progress_test.py +0 -0
  181. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/queries.py +0 -0
  182. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/tasks/queries_test.py +0 -0
  183. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/dashboard.spec.js +0 -0
  184. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/dashboard.test.js +0 -0
  185. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/favicon.js +0 -0
  186. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/favicon.spec.js +0 -0
  187. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/files.html +0 -0
  188. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/files.spec.js +0 -0
  189. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/index.html +0 -0
  190. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/index.js +0 -0
  191. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/logs.html +0 -0
  192. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/logs.test.js +0 -0
  193. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/mancha.js +0 -0
  194. {lemming_cli-0.4.6 → lemming_cli-0.5.0}/src/lemming/web/screenshots.spec.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lemming-cli
3
- Version: 0.4.6
3
+ Version: 0.5.0
4
4
  Summary: An autonomous, iterative task runner for AI coding agents.
5
5
  Project-URL: Homepage, https://github.com/owahltinez/lemming
6
6
  Project-URL: Repository, https://github.com/owahltinez/lemming
@@ -10,6 +10,7 @@ License-File: LICENSE
10
10
  Keywords: agents,ai,antigravity,automation,claude,opencode
11
11
  Requires-Python: >=3.12
12
12
  Requires-Dist: beautifulsoup4>=4.13.3
13
+ Requires-Dist: click-agentcli>=0.3.0
13
14
  Requires-Dist: click>=8.0.0
14
15
  Requires-Dist: fastapi>=0.124.4
15
16
  Requires-Dist: markdownify>=0.14.1
@@ -112,10 +113,19 @@ runner, and the agent's closing message comes back on stdout.
112
113
  # Delegate one task to a different agent, e.g. to spare another's quota
113
114
  lemming exec "Fix the flaky test in runner_test.py" --runner codex
114
115
 
116
+ # Allow up to three total attempts for transient runner failures
117
+ lemming exec "Fix the flaky test in runner_test.py" --retries 3
118
+
115
119
  # Pipe a longer handoff instead of fighting shell quoting
116
- cat handoff.md | lemming exec -f - --runner agy
120
+ cat handoff.md | lemming -v exec -f - --runner agy
117
121
  ```
118
122
 
123
+ The runner receives no conversation history and cannot be steered mid-run.
124
+ Make the handoff self-contained: state the goal, hard constraints, relevant
125
+ findings and evidence, approaches ruled out and why, and the definition of done
126
+ and verification. The installed skill includes a complete here-document
127
+ example for orchestrating agents.
128
+
119
129
  With no description there is nothing for a task runner to do, so only the
120
130
  reviews run — against work that already exists.
121
131
 
@@ -124,7 +134,7 @@ reviews run — against work that already exists.
124
134
  lemming exec --review readability
125
135
 
126
136
  # Review someone else's branch, checked out in a worktree
127
- lemming exec -C ../review-worktree --review testing --scope main...HEAD
137
+ lemming -C ../review-worktree exec --review testing --scope main...HEAD
128
138
 
129
139
  # Do the work, then gate it
130
140
  lemming exec "Add pagination to the tasks API" --review all
@@ -135,13 +145,14 @@ which is resolved to the files it changed. It defaults to uncommitted work, or
135
145
  to the whole tree outside a git repository.
136
146
 
137
147
  Each run is self-contained: nothing is read from the project's roadmap or its
138
- local hooks, one agent run is attempted, and the run's state directory is
139
- removed unless it failed — in which case it is kept, and its path printed, so
140
- the log can be read. Kept directories live in `~/.local/lemming/exec-*` and are
141
- retired automatically a week later, so a recent failure is always still there to
142
- inspect. Stdout carries the agent's message alone and everything else goes to
143
- stderr, so the output can be consumed directly. Stderr reports the ephemeral
144
- tasks file and task ID at startup; pass those to
148
+ local hooks, it runs only the requested task and reviews, and its state
149
+ directory is removed unless it failed — in which case it is kept, and its path
150
+ printed, so the log can be read. Kept directories live in
151
+ `~/.local/lemming/exec-*` and are retired automatically a week later, so a
152
+ recent failure is always still there to inspect. Stdout carries the agent's
153
+ message alone and everything else goes to stderr, so the output can be consumed
154
+ directly. Stderr reports the ephemeral tasks file and task ID at startup; pass
155
+ those to
145
156
  `lemming --tasks-file <path> status <id>` or `logs <id>` to inspect an active
146
157
  run. Use the global verbose flag (`lemming -v exec ...`) to stream runner
147
158
  activity.
@@ -149,25 +160,40 @@ activity.
149
160
  Note that the agent runs unattended (`--yolo` by default), so it does not
150
161
  inherit the permission prompts of whatever launched it.
151
162
 
163
+ `--retries N` includes the first attempt and defaults to 1. Retries reuse the
164
+ same task state and progress after incomplete runs, timeouts, launch errors, or
165
+ non-zero exits. An explicit failure or cancellation, an interruption, or a
166
+ failed review stops immediately. Review-only runs cannot be retried.
167
+
168
+ Concurrent writing runs must not share a checkout. Lemming stays independent of
169
+ Git, Mercurial, and other version control systems: the caller creates a separate
170
+ working copy with the repository's existing VCS, passes it with
171
+ `lemming -C <working-copy> -v exec ...`, and owns result recovery and cleanup.
172
+ For Git, stop when the source checkout has modified or untracked files instead
173
+ of silently excluding them. Retain every isolated working copy until its changes
174
+ have been recovered. The packaged skill includes a minimal Git example.
175
+
152
176
  ### Teaching Your Agent to Use It
153
177
 
154
178
  Lemming ships an Agent Skill so that an agent already running — Codex, Claude
155
179
  Code, Gemini CLI, Cursor — can reach any other agent CLI without knowing how
156
- each one spells its flags. Install it once:
180
+ each one spells its flags. Install or refresh it with:
157
181
 
158
182
  ```bash
159
- # Cross-tool location (~/.agents/skills), and report any tool-specific ones
183
+ # Cross-tool location plus every detected tool-specific location
160
184
  lemming skill install
161
185
 
162
- # Cover detected tools that require their own skill directory too
163
- lemming skill install --all
164
-
165
186
  # This repository only
166
187
  lemming skill install --to .agents/skills
188
+
189
+ # Inspect every supported location
190
+ lemming skill status
167
191
  ```
168
192
 
169
- `lemming skill uninstall` reverses it. Both refuse to touch a directory that
170
- does not hold this skill, so a mistyped `--to` fails instead of deleting work.
193
+ `lemming skill uninstall` reverses the default installation sweep. Both refuse
194
+ to touch a directory that does not hold this skill, so a mistyped `--to` fails
195
+ instead of deleting work. Use `--link` on a durable installation to track
196
+ package upgrades without reinstalling.
171
197
 
172
198
  ---
173
199
 
@@ -209,9 +235,9 @@ loops through each pending task:
209
235
  `lemming complete` or `lemming fail`. Agents can also schedule new tasks
210
236
  with `lemming add`, breaking down complex work into smaller steps that
211
237
  Lemming will pick up automatically.
212
- 4. **Retry or advance**: On failure, Lemming retries the task (up to
213
- `--retries`) with accumulated progress as context, so the agent learns from
214
- previous attempts. On success, it moves to the next task.
238
+ 4. **Retry or advance**: On failure, Lemming retries the task up to the
239
+ configured roadmap limit, with accumulated progress as context so the agent
240
+ learns from previous attempts. On success, it moves to the next task.
215
241
  5. **Orchestration**: After each task, Lemming can run one or more
216
242
  **Orchestrator Hooks** (like the built-in `roadmap` hook) to evaluate the
217
243
  results and adapt the roadmap if needed. Hooks are enabled by default but
@@ -411,19 +437,21 @@ These come before the subcommand and apply to all of them.
411
437
  revision range is resolved to the files it changed. Defaults to uncommitted
412
438
  work, or the whole tree outside a repository.
413
439
  - `--runner`, `--model`: Which agent CLI and model to use.
440
+ - `--retries`: Maximum total task-runner attempts, including the first
441
+ (default 1). Does not retry review-only runs.
414
442
  - `--time-limit`: Minutes before the agent is killed (default 60, 0 for no
415
443
  limit).
416
444
  - `--yolo/--no-yolo`: Run the agent unattended (default: True).
417
445
  - `--keep`: Keep the run's state directory even when it succeeds.
418
446
  - **`skill install`**: Install the packaged Agent Skill so agents discover
419
- Lemming. Writes to `~/.agents/skills` by default and names any tool-specific
420
- directories it found.
447
+ Lemming. Writes to `~/.agents/skills` and every detected tool-specific
448
+ directory by default. Re-running it refreshes existing Lemming copies.
421
449
  - `--to <dir>`: Install into a specific skills directory.
422
- - `--all`: Also cover every detected tool's own skills directory.
423
450
  - `--link`: Symlink instead of copying, so upgrades take effect immediately.
424
- - `--force`: Replace an existing installation of this skill.
425
451
  - `--dry-run`: Print what would happen, refusals included.
426
- - **`skill uninstall`**: Remove it again. Same `--to`, `--all`, and `--dry-run`.
452
+ - **`skill uninstall`**: Remove it again. Supports `--to` and `--dry-run`.
453
+ - **`skill status`**: Show every supported location and installation state.
454
+ Supports `--json` for structured output.
427
455
  - **`stop`**: Stop the running loop and its runner.
428
456
  - `--after-current-task`: Drain instead — let the running task finish, then
429
457
  stop before claiming another. This is the safe way to change the runner or
@@ -90,10 +90,19 @@ runner, and the agent's closing message comes back on stdout.
90
90
  # Delegate one task to a different agent, e.g. to spare another's quota
91
91
  lemming exec "Fix the flaky test in runner_test.py" --runner codex
92
92
 
93
+ # Allow up to three total attempts for transient runner failures
94
+ lemming exec "Fix the flaky test in runner_test.py" --retries 3
95
+
93
96
  # Pipe a longer handoff instead of fighting shell quoting
94
- cat handoff.md | lemming exec -f - --runner agy
97
+ cat handoff.md | lemming -v exec -f - --runner agy
95
98
  ```
96
99
 
100
+ The runner receives no conversation history and cannot be steered mid-run.
101
+ Make the handoff self-contained: state the goal, hard constraints, relevant
102
+ findings and evidence, approaches ruled out and why, and the definition of done
103
+ and verification. The installed skill includes a complete here-document
104
+ example for orchestrating agents.
105
+
97
106
  With no description there is nothing for a task runner to do, so only the
98
107
  reviews run — against work that already exists.
99
108
 
@@ -102,7 +111,7 @@ reviews run — against work that already exists.
102
111
  lemming exec --review readability
103
112
 
104
113
  # Review someone else's branch, checked out in a worktree
105
- lemming exec -C ../review-worktree --review testing --scope main...HEAD
114
+ lemming -C ../review-worktree exec --review testing --scope main...HEAD
106
115
 
107
116
  # Do the work, then gate it
108
117
  lemming exec "Add pagination to the tasks API" --review all
@@ -113,13 +122,14 @@ which is resolved to the files it changed. It defaults to uncommitted work, or
113
122
  to the whole tree outside a git repository.
114
123
 
115
124
  Each run is self-contained: nothing is read from the project's roadmap or its
116
- local hooks, one agent run is attempted, and the run's state directory is
117
- removed unless it failed — in which case it is kept, and its path printed, so
118
- the log can be read. Kept directories live in `~/.local/lemming/exec-*` and are
119
- retired automatically a week later, so a recent failure is always still there to
120
- inspect. Stdout carries the agent's message alone and everything else goes to
121
- stderr, so the output can be consumed directly. Stderr reports the ephemeral
122
- tasks file and task ID at startup; pass those to
125
+ local hooks, it runs only the requested task and reviews, and its state
126
+ directory is removed unless it failed — in which case it is kept, and its path
127
+ printed, so the log can be read. Kept directories live in
128
+ `~/.local/lemming/exec-*` and are retired automatically a week later, so a
129
+ recent failure is always still there to inspect. Stdout carries the agent's
130
+ message alone and everything else goes to stderr, so the output can be consumed
131
+ directly. Stderr reports the ephemeral tasks file and task ID at startup; pass
132
+ those to
123
133
  `lemming --tasks-file <path> status <id>` or `logs <id>` to inspect an active
124
134
  run. Use the global verbose flag (`lemming -v exec ...`) to stream runner
125
135
  activity.
@@ -127,25 +137,40 @@ activity.
127
137
  Note that the agent runs unattended (`--yolo` by default), so it does not
128
138
  inherit the permission prompts of whatever launched it.
129
139
 
140
+ `--retries N` includes the first attempt and defaults to 1. Retries reuse the
141
+ same task state and progress after incomplete runs, timeouts, launch errors, or
142
+ non-zero exits. An explicit failure or cancellation, an interruption, or a
143
+ failed review stops immediately. Review-only runs cannot be retried.
144
+
145
+ Concurrent writing runs must not share a checkout. Lemming stays independent of
146
+ Git, Mercurial, and other version control systems: the caller creates a separate
147
+ working copy with the repository's existing VCS, passes it with
148
+ `lemming -C <working-copy> -v exec ...`, and owns result recovery and cleanup.
149
+ For Git, stop when the source checkout has modified or untracked files instead
150
+ of silently excluding them. Retain every isolated working copy until its changes
151
+ have been recovered. The packaged skill includes a minimal Git example.
152
+
130
153
  ### Teaching Your Agent to Use It
131
154
 
132
155
  Lemming ships an Agent Skill so that an agent already running — Codex, Claude
133
156
  Code, Gemini CLI, Cursor — can reach any other agent CLI without knowing how
134
- each one spells its flags. Install it once:
157
+ each one spells its flags. Install or refresh it with:
135
158
 
136
159
  ```bash
137
- # Cross-tool location (~/.agents/skills), and report any tool-specific ones
160
+ # Cross-tool location plus every detected tool-specific location
138
161
  lemming skill install
139
162
 
140
- # Cover detected tools that require their own skill directory too
141
- lemming skill install --all
142
-
143
163
  # This repository only
144
164
  lemming skill install --to .agents/skills
165
+
166
+ # Inspect every supported location
167
+ lemming skill status
145
168
  ```
146
169
 
147
- `lemming skill uninstall` reverses it. Both refuse to touch a directory that
148
- does not hold this skill, so a mistyped `--to` fails instead of deleting work.
170
+ `lemming skill uninstall` reverses the default installation sweep. Both refuse
171
+ to touch a directory that does not hold this skill, so a mistyped `--to` fails
172
+ instead of deleting work. Use `--link` on a durable installation to track
173
+ package upgrades without reinstalling.
149
174
 
150
175
  ---
151
176
 
@@ -187,9 +212,9 @@ loops through each pending task:
187
212
  `lemming complete` or `lemming fail`. Agents can also schedule new tasks
188
213
  with `lemming add`, breaking down complex work into smaller steps that
189
214
  Lemming will pick up automatically.
190
- 4. **Retry or advance**: On failure, Lemming retries the task (up to
191
- `--retries`) with accumulated progress as context, so the agent learns from
192
- previous attempts. On success, it moves to the next task.
215
+ 4. **Retry or advance**: On failure, Lemming retries the task up to the
216
+ configured roadmap limit, with accumulated progress as context so the agent
217
+ learns from previous attempts. On success, it moves to the next task.
193
218
  5. **Orchestration**: After each task, Lemming can run one or more
194
219
  **Orchestrator Hooks** (like the built-in `roadmap` hook) to evaluate the
195
220
  results and adapt the roadmap if needed. Hooks are enabled by default but
@@ -389,19 +414,21 @@ These come before the subcommand and apply to all of them.
389
414
  revision range is resolved to the files it changed. Defaults to uncommitted
390
415
  work, or the whole tree outside a repository.
391
416
  - `--runner`, `--model`: Which agent CLI and model to use.
417
+ - `--retries`: Maximum total task-runner attempts, including the first
418
+ (default 1). Does not retry review-only runs.
392
419
  - `--time-limit`: Minutes before the agent is killed (default 60, 0 for no
393
420
  limit).
394
421
  - `--yolo/--no-yolo`: Run the agent unattended (default: True).
395
422
  - `--keep`: Keep the run's state directory even when it succeeds.
396
423
  - **`skill install`**: Install the packaged Agent Skill so agents discover
397
- Lemming. Writes to `~/.agents/skills` by default and names any tool-specific
398
- directories it found.
424
+ Lemming. Writes to `~/.agents/skills` and every detected tool-specific
425
+ directory by default. Re-running it refreshes existing Lemming copies.
399
426
  - `--to <dir>`: Install into a specific skills directory.
400
- - `--all`: Also cover every detected tool's own skills directory.
401
427
  - `--link`: Symlink instead of copying, so upgrades take effect immediately.
402
- - `--force`: Replace an existing installation of this skill.
403
428
  - `--dry-run`: Print what would happen, refusals included.
404
- - **`skill uninstall`**: Remove it again. Same `--to`, `--all`, and `--dry-run`.
429
+ - **`skill uninstall`**: Remove it again. Supports `--to` and `--dry-run`.
430
+ - **`skill status`**: Show every supported location and installation state.
431
+ Supports `--json` for structured output.
405
432
  - **`stop`**: Stop the running loop and its runner.
406
433
  - `--after-current-task`: Drain instead — let the running task finish, then
407
434
  stop before claiming another. This is the safe way to change the runner or
@@ -24,6 +24,9 @@ agent run can hold.
24
24
  ```sh
25
25
  lemming -v exec "Fix the flaky heartbeat test in src/runner_test.py" --runner codex
26
26
 
27
+ # Opt into at most three total attempts for transient runner failures
28
+ lemming -v exec "Fix the flaky heartbeat test" --retries 3
29
+
27
30
  # Pipe a longer handoff rather than fighting shell quoting; no length limit
28
31
  cat handoff.md | lemming -v exec -f - --runner agy
29
32
  ```
@@ -48,8 +51,70 @@ The task runner is required to record its approach first and concise findings as
48
51
  it works. These entries are checkpoints rather than a continuous trace, so use
49
52
  verbose output or the log to tell whether it is actively making progress.
50
53
 
51
- The task starts with an empty context and sees nothing of your conversation, so
52
- give it everything it needs.
54
+ The runner receives no conversation history and cannot be steered mid-run. Give
55
+ it a self-contained handoff with:
56
+
57
+ - **Goal:** the concrete outcome to produce.
58
+ - **Hard constraints:** scope, compatibility, process, and safety requirements.
59
+ - **Relevant findings and evidence:** paths, commands, output, and prior
60
+ observations it should rely on.
61
+ - **Approaches ruled out and why:** decisions it should not revisit without new
62
+ evidence.
63
+ - **Definition of done and verification:** the required result and checks that
64
+ prove it works.
65
+
66
+ For example:
67
+
68
+ ```sh
69
+ lemming -v exec -f - --runner codex <<'EOF'
70
+ # Goal
71
+ Fix the race that lets two workers claim the same queued job.
72
+
73
+ # Hard constraints
74
+ - Keep the SQLite schema and public CLI unchanged.
75
+ - Make the smallest scoped change; do not reformat unrelated files.
76
+
77
+ # Relevant findings and evidence
78
+ - `uv run pytest tests/test_queue.py -k concurrent_claim` fails intermittently.
79
+ - `src/queue.py:claim_next` reads and updates in separate transactions.
80
+
81
+ # Approaches ruled out and why
82
+ - Do not add an in-process mutex; separate worker processes would bypass it.
83
+
84
+ # Definition of done and verification
85
+ - The concurrency test passes repeatedly and no existing behavior regresses.
86
+ - Run the focused test, then the full test suite and configured linter.
87
+ EOF
88
+ ```
89
+
90
+ ## Isolate concurrent writing runs
91
+
92
+ Concurrent writing runs must use separate working copies. Detect and use the
93
+ repository's existing version control system, then pass each copy with the
94
+ global `-C` option. The caller owns creation, result recovery, and cleanup;
95
+ Lemming remains independent of Git, Mercurial, and other VCSs.
96
+
97
+ For Git, refuse modified or untracked input rather than silently excluding it,
98
+ then create a worktree from committed `HEAD`:
99
+
100
+ ```sh
101
+ repo=$(git rev-parse --show-toplevel) || exit 1
102
+ test -z "$(git -C "$repo" status --porcelain)" || exit 1
103
+ worktree=$(mktemp -d "${TMPDIR:-/tmp}/lemming-exec.XXXXXX") || exit 1
104
+ branch="lemming/$(basename "$worktree")"
105
+ git -C "$repo" worktree add -b "$branch" "$worktree" HEAD || exit 1
106
+ printf 'Worktree: %s\nBranch: %s\n' "$worktree" "$branch"
107
+ lemming -C "$worktree" -v exec "<self-contained task>" --runner codex
108
+ ```
109
+
110
+ Retain the reported worktree after success, failure, timeout, or interruption.
111
+ After its changes have been committed, copied, or deliberately discarded, use
112
+ Git's non-forcing cleanup so dirty or unmerged work is not destroyed:
113
+
114
+ ```sh
115
+ git -C "$repo" worktree remove "$worktree"
116
+ git -C "$repo" branch -d "$branch"
117
+ ```
53
118
 
54
119
  ## Run a review
55
120
 
@@ -111,12 +176,14 @@ one off for the project.
111
176
  - **The agent runs unattended.** `--yolo` is the default, so it does not inherit
112
177
  the permission prompts of whatever launched it. Treat this as granting an
113
178
  agent unsupervised write access to the working directory.
114
- - **`exec` makes one attempt, no retry.** A failure is final. A roadmap task
115
- retries (3 by default) with its progress carried forward.
179
+ - **`exec` makes one attempt by default.** `--retries N` opts into at most `N`
180
+ total task attempts with progress carried forward. Explicit failure,
181
+ cancellation, interruption, and failed reviews stop immediately.
116
182
  - **Interrupting leaves partial edits.** Nothing is restored, so there is no
117
183
  atomicity to rely on.
118
184
  - **Concurrent runs in one checkout interleave their edits.** Give each writing
119
- run its own worktree and address it with `-C`.
185
+ run its own VCS-managed working copy and address it with `-C`; retain the copy
186
+ until its result is recovered.
120
187
  - **Every run reports its state.** The tasks file and task ID are printed on
121
188
  stderr at startup. Successful state is removed on exit unless `--keep` is
122
189
  used; failures keep their directory and print it again for recovery. Kept
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "lemming-cli"
7
- version = "0.4.6"
7
+ version = "0.5.0"
8
8
  description = "An autonomous, iterative task runner for AI coding agents."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -16,6 +16,7 @@ keywords = ["ai", "agents", "automation", "antigravity", "claude", "opencode"]
16
16
  dependencies = [
17
17
  "beautifulsoup4>=4.13.3",
18
18
  "click>=8.0.0",
19
+ "click-agentcli>=0.3.0",
19
20
  "fastapi>=0.124.4",
20
21
  "markdownify>=0.14.1",
21
22
  "pydantic>=2.12.5",
@@ -30,6 +30,9 @@ ALL_REVIEWS = "all"
30
30
  # large enough that reading it whole is wasteful.
31
31
  MAX_LOG_TAIL_BYTES = 256 * 1024
32
32
 
33
+ # Log header used to isolate the final runner or review output.
34
+ ATTEMPT_HEADER = "--- Attempt started at "
35
+
33
36
 
34
37
  def _read_log_tail(
35
38
  log_file: pathlib.Path, max_bytes: int = MAX_LOG_TAIL_BYTES
@@ -41,17 +44,20 @@ def _read_log_tail(
41
44
  max_bytes: Most bytes to read from the end of the file.
42
45
 
43
46
  Returns:
44
- The tail of the log, decoded leniently. A leading partial line is
47
+ The final attempt within the decoded tail. A leading partial line is
45
48
  harmless: the extractor skips anything that is not a whole event.
46
49
  """
47
50
  try:
48
51
  with open(log_file, "rb") as f:
49
52
  f.seek(0, 2)
50
53
  f.seek(max(0, f.tell() - max_bytes))
51
- return f.read().decode("utf-8", errors="replace")
54
+ text = f.read().decode("utf-8", errors="replace")
52
55
  except OSError:
53
56
  return ""
54
57
 
58
+ header = text.rfind(ATTEMPT_HEADER)
59
+ return text[header:] if header >= 0 else text
60
+
55
61
 
56
62
  def _resolve_prompt(description: str | None, file: typing.TextIO | None) -> str:
57
63
  """Returns the prompt text from an argument or a file.
@@ -237,6 +243,13 @@ def _task_status(
237
243
  default=60,
238
244
  help="Minutes before the agent is killed (0 disables the limit).",
239
245
  )
246
+ @click.option(
247
+ "--retries",
248
+ default=1,
249
+ type=click.IntRange(min=1),
250
+ show_default=True,
251
+ help="Maximum total task-runner attempts, including the first.",
252
+ )
240
253
  @click.option(
241
254
  "--yolo/--no-yolo",
242
255
  default=True,
@@ -258,6 +271,7 @@ def exec_command(
258
271
  runner_name: str | None,
259
272
  model_name: str | None,
260
273
  time_limit: int,
274
+ retries: int,
261
275
  yolo: bool,
262
276
  keep: bool,
263
277
  runner_args: tuple,
@@ -285,6 +299,10 @@ def exec_command(
285
299
  if description or file or not reviews
286
300
  else None
287
301
  )
302
+ if prompt is None and retries > 1:
303
+ raise click.UsageError(
304
+ "--retries only applies when exec has a task description."
305
+ )
288
306
 
289
307
  # The runner must edit the caller's workspace, never the throwaway
290
308
  # directory that only holds this run's bookkeeping.
@@ -304,9 +322,8 @@ def exec_command(
304
322
  return
305
323
  scope_text = scope.describe(entries)
306
324
 
307
- # A one-shot spends one agent run: a silent retry would multiply the
308
- # caller's cost without their say.
309
- config = tasks.RoadmapConfig(retries=1, time_limit=time_limit)
325
+ # Extra attempts require opt-in because each can incur runner cost.
326
+ config = tasks.RoadmapConfig(retries=retries, time_limit=time_limit)
310
327
  if runner_name:
311
328
  config.runner = runner_name
312
329
  if model_name: