lemming-cli 0.7.2__tar.gz → 0.8.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 (193) hide show
  1. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/.github/workflows/ci.yml +1 -1
  2. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/PKG-INFO +24 -22
  3. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/README.md +23 -21
  4. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/SKILL.md +8 -8
  5. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/docs/EVALS.md +3 -0
  6. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/docs/HOOKS.md +27 -17
  7. lemming_cli-0.8.0/docs/screenshots/dashboard-desktop.png +0 -0
  8. lemming_cli-0.8.0/docs/screenshots/dashboard-mobile.png +0 -0
  9. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/pyproject.toml +1 -1
  10. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/config.py +5 -2
  11. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/config_test.py +20 -0
  12. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/hooks.py +4 -2
  13. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/hooks_test.py +13 -33
  14. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/config.py +2 -2
  15. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/config_test.py +2 -1
  16. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/exec_cli.py +8 -37
  17. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/exec_cli_test.py +2 -2
  18. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/hooks.py +20 -19
  19. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/hooks_test.py +26 -48
  20. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/tasks.py +34 -2
  21. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/tasks_test.py +29 -0
  22. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/roadmap.py +96 -1
  23. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/roadmap_test.py +35 -1
  24. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/hooks.py +110 -95
  25. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/hooks_test.py +129 -76
  26. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/models.py +4 -0
  27. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/orchestrator.py +21 -17
  28. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/orchestrator_test.py +41 -4
  29. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/prompts/hooks/50-readability.md +21 -13
  30. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/prompts/hooks/70-ux.md +1 -1
  31. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/prompts/hooks/90-roadmap.md +12 -5
  32. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/prompts/taskrunner.md +17 -6
  33. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/runner.py +25 -9
  34. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/runner_test.py +46 -0
  35. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/operations.py +13 -0
  36. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/operations_test.py +27 -0
  37. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/dashboard.spec.js +1 -0
  38. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/favicon.spec.js +1 -0
  39. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/index.html +1 -1
  40. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/index.js +2 -2
  41. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/screenshots.spec.js +3 -0
  42. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/uv.lock +1 -1
  43. lemming_cli-0.7.2/docs/screenshots/dashboard-desktop.png +0 -0
  44. lemming_cli-0.7.2/docs/screenshots/dashboard-mobile.png +0 -0
  45. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/.dockerignore +0 -0
  46. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/.github/workflows/publish.yml +0 -0
  47. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/.github/workflows/screenshots.yml +0 -0
  48. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/.gitignore +0 -0
  49. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/.prettierignore +0 -0
  50. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/.prettierrc +0 -0
  51. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/Dockerfile +0 -0
  52. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/LICENSE +0 -0
  53. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/biome.json +0 -0
  54. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/docker-compose.yml +0 -0
  55. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/docs/WORKSPACE.md +0 -0
  56. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/docs/screenshots/task-log-desktop.png +0 -0
  57. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/docs/screenshots/task-log-mobile.png +0 -0
  58. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/package-lock.json +0 -0
  59. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/package.json +0 -0
  60. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/playwright.config.js +0 -0
  61. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/__init__.py +0 -0
  62. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/__init__.py +0 -0
  63. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/auth.py +0 -0
  64. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/auth_test.py +0 -0
  65. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/conftest.py +0 -0
  66. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/context.py +0 -0
  67. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/context_test.py +0 -0
  68. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/directories.py +0 -0
  69. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/directories_test.py +0 -0
  70. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/files.py +0 -0
  71. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/files_test.py +0 -0
  72. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/logging.py +0 -0
  73. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/logging_test.py +0 -0
  74. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/loop.py +0 -0
  75. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/loop_test.py +0 -0
  76. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/main.py +0 -0
  77. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/main_test.py +0 -0
  78. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/tasks.py +0 -0
  79. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/api/tasks_test.py +0 -0
  80. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/artifact_test.py +0 -0
  81. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/brief_test.py +0 -0
  82. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/__init__.py +0 -0
  83. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/artifact.py +0 -0
  84. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/brief.py +0 -0
  85. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/goal.py +0 -0
  86. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/goal_test.py +0 -0
  87. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/machine_output_test.py +0 -0
  88. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/main.py +0 -0
  89. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/main_test.py +0 -0
  90. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/operations.py +0 -0
  91. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/operations_test.py +0 -0
  92. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/progress.py +0 -0
  93. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/progress_test.py +0 -0
  94. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/readability_cli.py +0 -0
  95. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/readability_cli_test.py +0 -0
  96. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/skill.py +0 -0
  97. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/skill_test.py +0 -0
  98. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/cli/stop_test.py +0 -0
  99. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/conftest.py +0 -0
  100. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/__init__.py +0 -0
  101. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/__main__.py +0 -0
  102. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/cli.py +0 -0
  103. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/cli_test.py +0 -0
  104. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/container.py +0 -0
  105. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/container_test.py +0 -0
  106. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/fixtures.py +0 -0
  107. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/fixtures_test.py +0 -0
  108. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/harness.py +0 -0
  109. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/harness_test.py +0 -0
  110. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/metrics.py +0 -0
  111. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/metrics_test.py +0 -0
  112. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/base/README.md +0 -0
  113. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/base/calc/__init__.py +0 -0
  114. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/base/calc/legacy.py +0 -0
  115. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/base/calc/ops.py +0 -0
  116. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/base/calc/ops_test.py +0 -0
  117. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/clean/calc/ops.py +0 -0
  118. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/clean/calc/ops_test.py +0 -0
  119. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/dead-code/calc/ops.py +0 -0
  120. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/dead-code/calc/ops_test.py +0 -0
  121. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/duplicated-behavior/calc/ops.py +0 -0
  122. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/duplicated-behavior/calc/ops_test.py +0 -0
  123. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/false-reuse/calc/limits.py +0 -0
  124. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/false-reuse/calc/limits_test.py +0 -0
  125. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/lint-debt/calc/ops.py +0 -0
  126. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/readability/lint-debt/calc/ops_test.py +0 -0
  127. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/add-only/README.md +0 -0
  128. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/add-only/calc/__init__.py +0 -0
  129. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/add-only/calc/ops.py +0 -0
  130. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/add-only/calc/ops_test.py +0 -0
  131. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/buggy-subtract/README.md +0 -0
  132. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/__init__.py +0 -0
  133. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/ops.py +0 -0
  134. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/ops_test.py +0 -0
  135. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/full-ops/README.md +0 -0
  136. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/full-ops/calc/__init__.py +0 -0
  137. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/full-ops/calc/ops.py +0 -0
  138. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/full-ops/calc/ops_test.py +0 -0
  139. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/unwired-cli/README.md +0 -0
  140. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/__init__.py +0 -0
  141. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/cli.py +0 -0
  142. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/cli_test.py +0 -0
  143. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/ops.py +0 -0
  144. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/ops_test.py +0 -0
  145. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/task/hidden/stats/summary_hidden_test.py +0 -0
  146. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/task/summary/README.md +0 -0
  147. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/task/summary/stats/__init__.py +0 -0
  148. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/task/summary/stats/summary.py +0 -0
  149. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/projects/task/summary/stats/summary_test.py +0 -0
  150. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/readability.py +0 -0
  151. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/readability_test.py +0 -0
  152. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/report.py +0 -0
  153. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/report_test.py +0 -0
  154. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/scenarios.py +0 -0
  155. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/scenarios_test.py +0 -0
  156. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/suites.py +0 -0
  157. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/task.py +0 -0
  158. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/task_test.py +0 -0
  159. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/trial.py +0 -0
  160. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/evals/trial_test.py +0 -0
  161. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/integration_test.py +0 -0
  162. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/main.py +0 -0
  163. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/main_test.py +0 -0
  164. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/models_test.py +0 -0
  165. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/paths.py +0 -0
  166. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/paths_test.py +0 -0
  167. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/persistence.py +0 -0
  168. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/persistence_test.py +0 -0
  169. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/prompts/hooks/60-testing.md +0 -0
  170. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/prompts.py +0 -0
  171. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/prompts_test.py +0 -0
  172. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/providers.py +0 -0
  173. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/providers_test.py +0 -0
  174. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/scope.py +0 -0
  175. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/scope_test.py +0 -0
  176. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/shutdown.py +0 -0
  177. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/shutdown_integration_test.py +0 -0
  178. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/shutdown_test.py +0 -0
  179. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/__init__.py +0 -0
  180. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/lifecycle.py +0 -0
  181. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/lifecycle_test.py +0 -0
  182. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/limits.py +0 -0
  183. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/progress.py +0 -0
  184. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/progress_test.py +0 -0
  185. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/queries.py +0 -0
  186. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/tasks/queries_test.py +0 -0
  187. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/dashboard.test.js +0 -0
  188. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/favicon.js +0 -0
  189. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/files.html +0 -0
  190. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/files.spec.js +0 -0
  191. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/logs.html +0 -0
  192. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/logs.test.js +0 -0
  193. {lemming_cli-0.7.2 → lemming_cli-0.8.0}/src/lemming/web/mancha.js +0 -0
@@ -49,7 +49,7 @@ jobs:
49
49
 
50
50
  - name: Run Web tests
51
51
  run: |
52
- npx playwright install --with-deps
52
+ npx playwright install --with-deps chromium
53
53
  npm test
54
54
 
55
55
  - name: Build container image
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lemming-cli
3
- Version: 0.7.2
3
+ Version: 0.8.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
@@ -139,7 +139,7 @@ lemming exec --review readability
139
139
  lemming -C ../review-worktree exec --review testing --scope main...HEAD
140
140
 
141
141
  # Do the work, then gate it
142
- lemming exec "Add pagination to the tasks API" --review all
142
+ lemming exec "Add pagination to the tasks API" --review '*'
143
143
  ```
144
144
 
145
145
  `--scope` takes paths, which pass through untouched, or a git revision range,
@@ -265,9 +265,9 @@ tasks may not fully achieve the stated goal. **Orchestrator Hooks** address this
265
265
  by running custom agents or scripts after each task execution to evaluate
266
266
  results and adapt the roadmap.
267
267
 
268
- Lemming runs every hook it discovers on the filesystem (including the built-in
269
- `roadmap` hook). Hooks are plain Markdown files, and udev-style filename
270
- conventions control their behavior:
268
+ Lemming runs the hooks enabled for the project, which by default is only the
269
+ built-in `roadmap` hook; review hooks are opt-in. Hooks are plain Markdown
270
+ files, and udev-style filename conventions control their behavior:
271
271
 
272
272
  - **Ordering**: A numeric prefix sets the execution order (e.g. `10-lint.md`
273
273
  runs before `90-roadmap.md`); files without a prefix default to priority 50.
@@ -276,15 +276,15 @@ conventions control their behavior:
276
276
  - **Oneshot tasks**: A task added with `--oneshot` skips all hooks once it
277
277
  completes, so mechanical work (a typo fix, a version bump) does not pay for
278
278
  the full review lifecycle. Failure hooks still run if it fails.
279
- - **Disabling**: An empty file masks (disables) the hook of the same name from a
280
- lower-precedence layer.
279
+ - **Masking**: An empty file hides the hook of the same name from a
280
+ lower-precedence layer, even where it is enabled.
281
281
 
282
282
  ```bash
283
- # Disable a hook for this project (writes an empty .lemming/hooks/50-lint.md)
284
- lemming hooks disable lint
283
+ # Run the testing review after every task in this project
284
+ lemming hooks enable testing
285
285
 
286
- # Re-enable it (removes the mask file)
287
- lemming hooks enable lint
286
+ # Or pick the reviews for one task, replacing the project's ('' for none)
287
+ lemming add "Redesign the settings page" --hooks ux,testing
288
288
  ```
289
289
 
290
290
  ### Built-in Hooks ⚓️
@@ -329,10 +329,14 @@ See [docs/HOOKS.md](docs/HOOKS.md) for more details.
329
329
  Use the `config` and `hooks` commands to manage your project's execution loop:
330
330
 
331
331
  ```bash
332
- # List all hooks in execution order, with source and status
332
+ # List all hooks in execution order, marking the ones enabled by default
333
333
  lemming hooks list
334
334
 
335
- # View current project configuration (includes the active hooks)
335
+ # Enable or disable hooks for the project (stored in tasks.yml)
336
+ lemming hooks enable readability
337
+ lemming hooks disable readability
338
+
339
+ # View current project configuration (includes the enabled hooks)
336
340
  lemming config list
337
341
 
338
342
  # Persist configuration to tasks.yml
@@ -380,9 +384,10 @@ These come before the subcommand and apply to all of them.
380
384
  - **`goal [<text>]`**: Set or view the long-term goal shared by all tasks.
381
385
  Supports `-f/--file`.
382
386
  - **`add <desc>`**: Append a new task. Supports `--index`, `--runner`,
383
- `--model`, and `--oneshot`.
384
- - **`edit <id>`**: Modify a task's description, runner, model, position, or
385
- oneshot flag (`--oneshot/--no-oneshot`).
387
+ `--model`, `--oneshot`, and `--hooks` (the task's reviews, comma-separated;
388
+ inherited from `--parent` when omitted).
389
+ - **`edit <id>`**: Modify a task's description, runner, model, position,
390
+ oneshot flag (`--oneshot/--no-oneshot`), or reviews (`--hooks`).
386
391
  - **`brief <id> [text]`**: View or set a task's long-form brief. Unlike the
387
392
  description it has no length cap, and it is appended to the runner prompt
388
393
  automatically — the right home for measured timings, exact failing selectors,
@@ -410,12 +415,9 @@ These come before the subcommand and apply to all of them.
410
415
  - `set <key> <value>`: Update a setting. `set model default` clears a pinned
411
416
  model without touching the runner.
412
417
  - **`hooks`**: Manage orchestrator hooks.
413
- - `list`: View available and active hooks.
414
- - `install`: Install built-in hooks to the global directory.
415
- - `enable <name>...`: Activate one or more hooks.
416
- - `disable <name>...`: Deactivate one or more hooks.
417
- - `set <name>...`: Set the exact list of active hooks.
418
- - `reset`: Restore default hooks (run all available).
418
+ - `list`: View available hooks and the ones enabled for the project.
419
+ - `enable <name>...`: Run one or more hooks after every task.
420
+ - `disable <name>...`: Stop running one or more hooks by default.
419
421
  - **`readability`**: Code quality tool for style guide adherence, wrapping the
420
422
  [readability](https://github.com/owahltinez/readability) package. Ruff and
421
423
  Pyrefly run with bundled Google-style defaults unless the target project
@@ -119,7 +119,7 @@ lemming exec --review readability
119
119
  lemming -C ../review-worktree exec --review testing --scope main...HEAD
120
120
 
121
121
  # Do the work, then gate it
122
- lemming exec "Add pagination to the tasks API" --review all
122
+ lemming exec "Add pagination to the tasks API" --review '*'
123
123
  ```
124
124
 
125
125
  `--scope` takes paths, which pass through untouched, or a git revision range,
@@ -245,9 +245,9 @@ tasks may not fully achieve the stated goal. **Orchestrator Hooks** address this
245
245
  by running custom agents or scripts after each task execution to evaluate
246
246
  results and adapt the roadmap.
247
247
 
248
- Lemming runs every hook it discovers on the filesystem (including the built-in
249
- `roadmap` hook). Hooks are plain Markdown files, and udev-style filename
250
- conventions control their behavior:
248
+ Lemming runs the hooks enabled for the project, which by default is only the
249
+ built-in `roadmap` hook; review hooks are opt-in. Hooks are plain Markdown
250
+ files, and udev-style filename conventions control their behavior:
251
251
 
252
252
  - **Ordering**: A numeric prefix sets the execution order (e.g. `10-lint.md`
253
253
  runs before `90-roadmap.md`); files without a prefix default to priority 50.
@@ -256,15 +256,15 @@ conventions control their behavior:
256
256
  - **Oneshot tasks**: A task added with `--oneshot` skips all hooks once it
257
257
  completes, so mechanical work (a typo fix, a version bump) does not pay for
258
258
  the full review lifecycle. Failure hooks still run if it fails.
259
- - **Disabling**: An empty file masks (disables) the hook of the same name from a
260
- lower-precedence layer.
259
+ - **Masking**: An empty file hides the hook of the same name from a
260
+ lower-precedence layer, even where it is enabled.
261
261
 
262
262
  ```bash
263
- # Disable a hook for this project (writes an empty .lemming/hooks/50-lint.md)
264
- lemming hooks disable lint
263
+ # Run the testing review after every task in this project
264
+ lemming hooks enable testing
265
265
 
266
- # Re-enable it (removes the mask file)
267
- lemming hooks enable lint
266
+ # Or pick the reviews for one task, replacing the project's ('' for none)
267
+ lemming add "Redesign the settings page" --hooks ux,testing
268
268
  ```
269
269
 
270
270
  ### Built-in Hooks ⚓️
@@ -309,10 +309,14 @@ See [docs/HOOKS.md](docs/HOOKS.md) for more details.
309
309
  Use the `config` and `hooks` commands to manage your project's execution loop:
310
310
 
311
311
  ```bash
312
- # List all hooks in execution order, with source and status
312
+ # List all hooks in execution order, marking the ones enabled by default
313
313
  lemming hooks list
314
314
 
315
- # View current project configuration (includes the active hooks)
315
+ # Enable or disable hooks for the project (stored in tasks.yml)
316
+ lemming hooks enable readability
317
+ lemming hooks disable readability
318
+
319
+ # View current project configuration (includes the enabled hooks)
316
320
  lemming config list
317
321
 
318
322
  # Persist configuration to tasks.yml
@@ -360,9 +364,10 @@ These come before the subcommand and apply to all of them.
360
364
  - **`goal [<text>]`**: Set or view the long-term goal shared by all tasks.
361
365
  Supports `-f/--file`.
362
366
  - **`add <desc>`**: Append a new task. Supports `--index`, `--runner`,
363
- `--model`, and `--oneshot`.
364
- - **`edit <id>`**: Modify a task's description, runner, model, position, or
365
- oneshot flag (`--oneshot/--no-oneshot`).
367
+ `--model`, `--oneshot`, and `--hooks` (the task's reviews, comma-separated;
368
+ inherited from `--parent` when omitted).
369
+ - **`edit <id>`**: Modify a task's description, runner, model, position,
370
+ oneshot flag (`--oneshot/--no-oneshot`), or reviews (`--hooks`).
366
371
  - **`brief <id> [text]`**: View or set a task's long-form brief. Unlike the
367
372
  description it has no length cap, and it is appended to the runner prompt
368
373
  automatically — the right home for measured timings, exact failing selectors,
@@ -390,12 +395,9 @@ These come before the subcommand and apply to all of them.
390
395
  - `set <key> <value>`: Update a setting. `set model default` clears a pinned
391
396
  model without touching the runner.
392
397
  - **`hooks`**: Manage orchestrator hooks.
393
- - `list`: View available and active hooks.
394
- - `install`: Install built-in hooks to the global directory.
395
- - `enable <name>...`: Activate one or more hooks.
396
- - `disable <name>...`: Deactivate one or more hooks.
397
- - `set <name>...`: Set the exact list of active hooks.
398
- - `reset`: Restore default hooks (run all available).
398
+ - `list`: View available hooks and the ones enabled for the project.
399
+ - `enable <name>...`: Run one or more hooks after every task.
400
+ - `disable <name>...`: Stop running one or more hooks by default.
399
401
  - **`readability`**: Code quality tool for style guide adherence, wrapping the
400
402
  [readability](https://github.com/owahltinez/readability) package. Ruff and
401
403
  Pyrefly run with bundled Google-style defaults unless the target project
@@ -124,8 +124,8 @@ reviews run — against work that already exists.
124
124
  ```sh
125
125
  lemming exec --review readability # uncommitted work
126
126
  lemming exec --review testing --scope main...HEAD
127
- lemming exec --review all --scope src/api/
128
- lemming exec "Add pagination" --review all # do the work, then gate it
127
+ lemming exec --review '*' --scope src/api/
128
+ lemming exec "Add pagination" --review '*' # do the work, then gate it
129
129
 
130
130
  # Someone else's branch, in a worktree so your own tree is untouched
131
131
  git worktree add /tmp/pr-123 && (cd /tmp/pr-123 && gh pr checkout 123)
@@ -162,12 +162,12 @@ The loop shares one workspace across all its tasks, so isolate the **whole run**
162
162
  — one branch or worktree created before `lemming run`, driven with
163
163
  `lemming -C <worktree> run` — never one per task.
164
164
 
165
- **Mark mechanical tasks `--oneshot`.** After every other task, review hooks run
166
- and a roadmap hook may revise the queue; each hook is a full agent run, so a
167
- typo fix, rename, version bump, or cleanup without `--oneshot` takes several
168
- times longer than the work itself. Reserve hooked tasks for changes in
169
- behavior. `lemming hooks list` shows the hooks; `lemming hooks disable <name>`
170
- turns one off for the project.
165
+ **Mark mechanical tasks `--oneshot`.** After every other task, a roadmap hook
166
+ may revise the queue and any enabled reviews run; each hook is a full agent
167
+ run, so a typo fix, rename, version bump, or cleanup without `--oneshot` takes
168
+ several times longer than the work itself. Reviews are opt-in: pick them for
169
+ one task with `lemming add ... --hooks testing,ux`, or for every task with
170
+ `lemming hooks enable <name>`. `lemming hooks list` shows which are enabled.
171
171
 
172
172
  ### Supervise in milestones
173
173
 
@@ -153,6 +153,9 @@ running a constant policy in either direction should score 50% across the pair.
153
153
  `false-reuse-restraint` exists for exactly this reason: it is the inverse of
154
154
  `consolidate-or-report-live-duplication`, with two functions that share a shape
155
155
  but not a concept, and folding them together is the failure.
156
+ `finding-is-not-a-task` is the roadmap suite's inverse of
157
+ `follow-up-without-code-changes`: the finished task logs optional polish ideas
158
+ rather than a bug, and scheduling them is the failure.
156
159
 
157
160
  The `task` suite carries the same idea into the code an agent writes rather than
158
161
  reviews. `minimal-change-restraint` hands it one well-specified function to add
@@ -36,10 +36,10 @@ conventions control their behavior:
36
36
  failure hooks, so recovery is never skipped. Note that `roadmap` is skipped
37
37
  too: if a oneshot task is the last one in the queue, the run ends without a
38
38
  goal audit, so nothing is queued to carry the goal further.
39
- - **Masking**: An empty file disables the hook of the same name from a
40
- lower-precedence layer (see below). For example, an empty
41
- `.lemming/hooks/readability.md` disables the built-in `readability` hook for
42
- that project.
39
+ - **Masking**: An empty file hides the hook of the same name from a
40
+ lower-precedence layer (see below), even where a project or task enables it.
41
+ For example, an empty `.lemming/hooks/readability.md` hides the built-in
42
+ `readability` hook from that project.
43
43
  - **Overriding**: A non-empty file replaces the hook of the same name from a
44
44
  lower-precedence layer. The winning filename also determines the priority, so
45
45
  keep the `9x-` prefix when overriding a failure hook (e.g. `90-roadmap.md`),
@@ -47,6 +47,9 @@ conventions control their behavior:
47
47
 
48
48
  ## Built-in Hooks
49
49
 
50
+ Only `roadmap` runs by default; the review hooks are opt-in per project or task
51
+ (see [Enabling and Disabling](#enabling-and-disabling)).
52
+
50
53
  ### `roadmap` (Default)
51
54
 
52
55
  The primary mechanism for autonomous project management. It analyzes the results
@@ -175,25 +178,32 @@ are issues, add a new task to fix them.
175
178
 
176
179
  ### Enabling and Disabling
177
180
 
178
- Lemming runs every hook it discovers that is not masked. There is no hook
179
- configuration in `tasks.yml`; the filesystem is the single source of truth.
180
-
181
- The `hooks` command group provides shortcuts for managing project masks:
181
+ Each project lists the hooks it runs in `tasks.yml` under `config.hooks`, which
182
+ defaults to `[roadmap]`: review hooks are opt-in. Manage the list with:
182
183
 
183
184
  ```bash
184
- # Disable a hook for this project (writes an empty .lemming/hooks/50-lint.md)
185
+ # Run the lint review after every task in this project
186
+ lemming hooks enable lint
187
+
188
+ # Stop running it by default
185
189
  lemming hooks disable lint
190
+ ```
186
191
 
187
- # Re-enable it (removes the mask file)
188
- lemming hooks enable lint
192
+ A task can pick its own reviews, which replace the project's for that task:
193
+
194
+ ```bash
195
+ lemming add "Redesign the settings page" --hooks ux,testing
196
+ lemming edit <id> --hooks '' # no reviews for this task
197
+ lemming edit <id> --hooks '*' # every review available now
189
198
  ```
190
199
 
191
- These commands validate the hook names before changing anything and refuse to
192
- touch files with content (a non-empty file is an override, not a mask). An empty
193
- file created by hand works just as well; the commands additionally keep the
194
- hook's priority in the mask filename so listings stay accurate.
200
+ `--hooks` accepts only reviews (priority below 90). Hooks at 90 and above, such
201
+ as `roadmap`, orchestrate the queue and always follow the project's list. A task
202
+ added with `--parent` and no `--hooks` inherits its parent's choice, so a split
203
+ keeps it. The same rules apply to built-in, global, and project hooks, and a
204
+ masked or missing hook never runs whatever the lists say.
195
205
 
196
- Hooks are re-discovered on every task execution, so changes are picked up
206
+ The selection is re-read every time hooks run, so changes are picked up
197
207
  dynamically by a running orchestrator loop.
198
208
 
199
209
  ### Available Variables
@@ -227,7 +237,7 @@ audit" describes the kind of review, not its extent.
227
237
  ### Listing Hooks
228
238
 
229
239
  You can see all hooks in execution order, with their priority, source layer, and
230
- status (disabled, runs on failure) using the CLI:
240
+ status (enabled for the project, masked, runs on failure) using the CLI:
231
241
 
232
242
  ```bash
233
243
  lemming hooks list
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "lemming-cli"
7
- version = "0.7.2"
7
+ version = "0.8.0"
8
8
  description = "An autonomous, iterative task runner for AI coding agents."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -36,11 +36,14 @@ def update_config(
36
36
  config: models.RoadmapConfig,
37
37
  project: str | None = None,
38
38
  ):
39
- """Replace the roadmap configuration and return the saved value."""
39
+ """Update the posted roadmap configuration fields and return the result."""
40
40
  tasks_file = context.resolve_tasks_file(request.app.state, project)
41
41
  with persistence.lock_tasks(tasks_file):
42
42
  data = persistence.load_tasks(tasks_file)
43
- data.config = config
43
+ # Omitted fields keep their value; the dashboard posts a subset
44
+ data.config = data.config.model_copy(
45
+ update=config.model_dump(exclude_unset=True)
46
+ )
44
47
  persistence.save_tasks(tasks_file, data)
45
48
  return data.config
46
49
 
@@ -1,5 +1,6 @@
1
1
  from unittest.mock import patch
2
2
 
3
+ from lemming import persistence
3
4
  from lemming.api import main
4
5
 
5
6
 
@@ -17,6 +18,25 @@ def test_run_loop(client, test_tasks):
17
18
  assert "--hook" not in args
18
19
 
19
20
 
21
+ def test_update_config_keeps_omitted_fields(client, test_tasks):
22
+ """The dashboard posts a partial config; it must not reset the rest."""
23
+ data = persistence.load_tasks(test_tasks)
24
+ data.config.hooks = ["ux", "roadmap"]
25
+ data.config.model = "fast-model"
26
+ persistence.save_tasks(test_tasks, data)
27
+
28
+ response = client.post(
29
+ "/api/config", json={"runner": "codex", "retries": 5, "time_limit": 0}
30
+ )
31
+
32
+ assert response.status_code == 200
33
+ config = persistence.load_tasks(test_tasks).config
34
+ assert config.runner == "codex"
35
+ assert config.retries == 5
36
+ assert config.hooks == ["ux", "roadmap"]
37
+ assert config.model == "fast-model"
38
+
39
+
20
40
  def test_get_runners(client):
21
41
  response = client.get("/api/runners")
22
42
  assert response.status_code == 200
@@ -5,7 +5,7 @@ import pathlib
5
5
  import fastapi
6
6
  import pydantic
7
7
 
8
- from .. import hooks
8
+ from .. import hooks, persistence
9
9
  from . import context
10
10
 
11
11
  router = fastapi.APIRouter()
@@ -13,12 +13,14 @@ router = fastapi.APIRouter()
13
13
 
14
14
  def _hook_payload(tasks_file: pathlib.Path) -> list[dict]:
15
15
  """Serializes resolved hooks for API responses."""
16
+ enabled = persistence.load_tasks(tasks_file).config.hooks
16
17
  return [
17
18
  {
18
19
  "name": h.name,
19
20
  "priority": h.priority,
20
21
  "source": h.source,
21
22
  "masked": h.masked,
23
+ "enabled": not h.masked and h.name in enabled,
22
24
  "runs_on_failure": h.priority >= hooks.FAILURE_HOOK_PRIORITY,
23
25
  }
24
26
  for h in hooks.resolve_hooks(tasks_file)
@@ -45,7 +47,7 @@ def toggle_hook(
45
47
  toggle: HookToggle,
46
48
  project: str | None = None,
47
49
  ):
48
- """Enable or disable a hook by removing or creating its project mask."""
50
+ """Add or remove a hook from those enabled for the project."""
49
51
  tasks_file = context.resolve_tasks_file(request.app.state, project)
50
52
  try:
51
53
  if toggle.enabled:
@@ -1,3 +1,4 @@
1
+ from lemming import persistence
1
2
  from lemming.api import main
2
3
 
3
4
 
@@ -30,8 +31,6 @@ def test_list_hooks_with_project(client, test_tasks, monkeypatch, tmp_path):
30
31
  root = main.app.state.root
31
32
  project_dir = root / "project1"
32
33
  project_dir.mkdir()
33
- tasks_file = project_dir / "tasks.yml"
34
- tasks_file.touch()
35
34
 
36
35
  response = client.get("/api/hooks", params={"project": "project1"})
37
36
 
@@ -40,29 +39,28 @@ def test_list_hooks_with_project(client, test_tasks, monkeypatch, tmp_path):
40
39
 
41
40
 
42
41
  def test_toggle_hook(client, test_tasks, monkeypatch, tmp_path):
43
- """Disabling a hook creates a project mask; enabling removes it."""
42
+ """Toggling a hook edits the project's default set, not mask files."""
44
43
  monkeypatch.setenv("LEMMING_HOME", str(tmp_path / "lemming_home"))
45
- # The mask filename keeps the hook's priority (roadmap is 90)
46
- mask = test_tasks.parent / ".lemming" / "hooks" / "90-roadmap.md"
47
44
 
48
- # Disable: the response reflects the mask and the file exists
49
- response = client.post(
50
- "/api/hooks", json={"name": "roadmap", "enabled": False}
51
- )
45
+ response = client.get("/api/hooks")
46
+ hooks = {h["name"]: h for h in response.json()}
47
+ assert hooks["roadmap"]["enabled"] is True
48
+ assert hooks["ux"]["enabled"] is False
49
+
50
+ response = client.post("/api/hooks", json={"name": "ux", "enabled": True})
52
51
  assert response.status_code == 200
53
52
  hooks = {h["name"]: h for h in response.json()}
54
- assert hooks["roadmap"]["masked"] is True
55
- assert mask.exists()
56
- assert mask.read_text(encoding="utf-8") == ""
53
+ assert hooks["ux"]["enabled"] is True
57
54
 
58
- # Enable: the mask is removed
59
55
  response = client.post(
60
- "/api/hooks", json={"name": "roadmap", "enabled": True}
56
+ "/api/hooks", json={"name": "roadmap", "enabled": False}
61
57
  )
62
58
  assert response.status_code == 200
63
59
  hooks = {h["name"]: h for h in response.json()}
60
+ assert hooks["roadmap"]["enabled"] is False
64
61
  assert hooks["roadmap"]["masked"] is False
65
- assert not mask.exists()
62
+ assert persistence.load_tasks(test_tasks).config.hooks == ["ux"]
63
+ assert not (test_tasks.parent / ".lemming").exists()
66
64
 
67
65
 
68
66
  def test_toggle_unknown_hook(client, test_tasks, monkeypatch, tmp_path):
@@ -75,21 +73,3 @@ def test_toggle_unknown_hook(client, test_tasks, monkeypatch, tmp_path):
75
73
 
76
74
  assert response.status_code == 400
77
75
  assert "not found" in response.json()["detail"]
78
-
79
-
80
- def test_toggle_hook_refuses_project_override(
81
- client, test_tasks, monkeypatch, tmp_path
82
- ):
83
- """Disabling a hook with a project override must not clobber it."""
84
- monkeypatch.setenv("LEMMING_HOME", str(tmp_path / "lemming_home"))
85
- local_hooks_dir = test_tasks.parent / ".lemming" / "hooks"
86
- local_hooks_dir.mkdir(parents=True)
87
- override = local_hooks_dir / "roadmap.md"
88
- override.write_text("custom prompt", encoding="utf-8")
89
-
90
- response = client.post(
91
- "/api/hooks", json={"name": "roadmap", "enabled": False}
92
- )
93
-
94
- assert response.status_code == 400
95
- assert override.read_text(encoding="utf-8") == "custom prompt"
@@ -27,8 +27,8 @@ def config_list(ctx: click.Context):
27
27
  click.echo(f" Retries: {c.retries}")
28
28
  click.echo(f" Time limit: {format_duration(c.time_limit)}")
29
29
 
30
- # Hooks are discovered from the filesystem, not stored in config
31
- active_hooks = hooks.list_hooks(tasks_file)
30
+ # Enabled hooks that resolve to an unmasked file, in execution order
31
+ active_hooks = hooks.select_hooks(tasks_file, c.hooks, None)
32
32
  click.echo(
33
33
  " Hooks: "
34
34
  f"{', '.join(active_hooks) if active_hooks else '(none)'}"
@@ -36,9 +36,10 @@ class TestCLIConfig(unittest.TestCase):
36
36
  )
37
37
  self.assertEqual(result.exit_code, 0)
38
38
  self.assertIn("Runner:", result.output)
39
- # Active hooks are discovered from the filesystem and displayed
39
+ # Only the project's enabled hooks are listed; reviews are opt-in
40
40
  self.assertIn("Hooks:", result.output)
41
41
  self.assertIn("roadmap", result.output)
42
+ self.assertNotIn("readability", result.output)
42
43
 
43
44
  def test_config_set(self):
44
45
  result = self.cli_runner.invoke(
@@ -17,15 +17,11 @@ import typing
17
17
 
18
18
  import click
19
19
 
20
- from .. import models, paths, persistence, runner, scope, shutdown
21
- from ..hooks import FAILURE_HOOK_PRIORITY, get_hook_priority, list_hooks
20
+ from .. import hooks, models, paths, persistence, runner, scope, shutdown
22
21
  from ..orchestrator import run_loop
23
22
  from ..tasks import lifecycle, limits, operations
24
23
  from .main import cli
25
24
 
26
- # Requests every review rather than making the caller name each one.
27
- ALL_REVIEWS = "all"
28
-
29
25
  # Ceiling on how much of the runner log is scanned for the closing message.
30
26
  # The message is at the end, and a log carrying every tool call can be
31
27
  # large enough that reading it whole is wasteful.
@@ -150,7 +146,7 @@ def _resolve_reviews(values: tuple[str, ...]) -> list[str]:
150
146
 
151
147
  Args:
152
148
  values: Requested reviews, each possibly comma-separated. The value
153
- "all" stands for every review.
149
+ "*" stands for every review.
154
150
 
155
151
  Returns:
156
152
  Review names ordered by hook priority.
@@ -158,38 +154,13 @@ def _resolve_reviews(values: tuple[str, ...]) -> list[str]:
158
154
  Raises:
159
155
  click.UsageError: If a name is unknown or orchestrates the queue.
160
156
  """
161
- requested = [
162
- name.strip()
163
- for value in values
164
- for name in value.split(",")
165
- if name.strip()
166
- ]
167
- available = list_hooks()
168
-
169
157
  # The 9x band orchestrates the queue: it revises the roadmap. Here the
170
158
  # roadmap holds one task and is deleted afterwards, so a hook that adds
171
159
  # tasks to it would set unbounded work running from a one-shot.
172
- reviews = [
173
- name
174
- for name in available
175
- if get_hook_priority(name) < FAILURE_HOOK_PRIORITY
176
- ]
177
-
178
- if ALL_REVIEWS in requested:
179
- return reviews
180
-
181
- for name in requested:
182
- if name in reviews:
183
- continue
184
- if name in available:
185
- raise click.UsageError(
186
- f"'{name}' orchestrates the roadmap and cannot run as a "
187
- "review. Use 'lemming run' for roadmap revision."
188
- )
189
- raise click.UsageError(
190
- f"Unknown review '{name}'. Available: {', '.join(reviews)}."
191
- )
192
- return [name for name in reviews if name in requested]
160
+ try:
161
+ return hooks.parse_reviews(values)
162
+ except ValueError as e:
163
+ raise click.UsageError(str(e)) from e
193
164
 
194
165
 
195
166
  def _task_status(
@@ -225,7 +196,7 @@ def _task_status(
225
196
  multiple=True,
226
197
  help=(
227
198
  "Reviews to run after the task, comma-separated or repeated. "
228
- "Use 'all' for every review. With no description, only the "
199
+ "Use '*' for every review. With no description, only the "
229
200
  "reviews run. Reviews may edit the working directory."
230
201
  ),
231
202
  )
@@ -291,7 +262,7 @@ def exec_command(
291
262
  lemming exec "Fix the flaky test in runner_test.py" --runner codex
292
263
  lemming exec --review readability
293
264
  lemming exec --review testing --scope main...HEAD
294
- lemming exec "Add pagination" --review all
265
+ lemming exec "Add pagination" --review '*'
295
266
  """
296
267
  verbose = ctx.obj["VERBOSE"]
297
268
  reviews = _resolve_reviews(review_values)
@@ -526,7 +526,7 @@ def test_review_refuses_the_orchestration_hook(repo):
526
526
 
527
527
 
528
528
  def test_review_all_excludes_the_orchestration_band(repo):
529
- """--review all must not require naming every review by hand."""
529
+ """--review '*' must not require naming every review by hand."""
530
530
  (repo / "committed.py").write_text("x = 2\n")
531
531
  headers = []
532
532
 
@@ -535,7 +535,7 @@ def test_review_all_excludes_the_orchestration_band(repo):
535
535
  return _finish("Reviewed.")(cmd, tasks_file, task_id, *args, **kwargs)
536
536
 
537
537
  with mock.patch("lemming.runner.run_with_heartbeat", record):
538
- CliRunner().invoke(main.cli, ["exec", "--review", "all"])
538
+ CliRunner().invoke(main.cli, ["exec", "--review", "*"])
539
539
 
540
540
  assert "Hook: roadmap" not in headers
541
541
  assert "Hook: readability" in headers