lemming-cli 0.2.0__tar.gz → 0.3.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.
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/PKG-INFO +29 -12
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/README.md +28 -11
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/docs/HOOKS.md +19 -4
- lemming_cli-0.3.0/docs/screenshots/dashboard-desktop.png +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/docs/screenshots/dashboard-mobile.png +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/package-lock.json +4 -4
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/package.json +1 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/pyproject.toml +1 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/hooks_test.py +3 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/tasks.py +35 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/tasks_test.py +34 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/tasks.py +231 -67
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/tasks_test.py +139 -6
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/readability.py +134 -2
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/readability_test.py +80 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/roadmap.py +162 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/roadmap_test.py +63 -5
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/trial_test.py +2 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/hooks_test.py +5 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/models.py +4 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/models_test.py +4 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/orchestrator.py +17 -4
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/orchestrator_test.py +38 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/persistence_test.py +23 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/prompts/hooks/50-readability.md +20 -7
- lemming_cli-0.3.0/src/lemming/prompts/hooks/70-ux.md +55 -0
- lemming_cli-0.3.0/src/lemming/prompts/hooks/90-roadmap.md +79 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/prompts.py +3 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/prompts_test.py +35 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/runner.py +36 -4
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/runner_test.py +53 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/__init__.py +9 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/lifecycle.py +38 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/lifecycle_test.py +24 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/operations.py +76 -4
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/operations_test.py +69 -3
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/queries.py +45 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/queries_test.py +22 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/dashboard.spec.js +62 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/dashboard.test.js +289 -19
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/files.html +4 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/index.html +106 -22
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/index.js +139 -24
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/logs.html +15 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/logs.test.js +29 -0
- lemming_cli-0.3.0/src/lemming/web/mancha.js +58 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/screenshots.spec.js +0 -1
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/uv.lock +1 -1
- lemming_cli-0.2.0/docs/screenshots/dashboard-desktop.png +0 -0
- lemming_cli-0.2.0/src/lemming/prompts/hooks/90-roadmap.md +0 -61
- lemming_cli-0.2.0/src/lemming/web/mancha.js +0 -58
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/.github/workflows/publish.yml +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/.github/workflows/screenshots.yml +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/.gitignore +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/.prettierignore +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/.prettierrc +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/Dockerfile +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/LICENSE +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/biome.json +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/docker-compose.yml +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/docs/EVALS.md +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/docs/screenshots/task-log-desktop.png +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/docs/screenshots/task-log-mobile.png +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/playwright.config.js +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/__init__.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/__init__.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/auth.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/auth_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/config.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/config_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/conftest.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/context.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/context_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/directories.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/directories_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/files.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/files_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/hooks.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/logging.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/logging_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/loop.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/loop_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/main.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api/main_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/api_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/__init__.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/config.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/config_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/goal.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/goal_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/hooks.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/hooks_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/main.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/main_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/operations.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/operations_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/progress.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/progress_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/readability_cli.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli/readability_cli_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/cli_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/conftest.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/__init__.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/__main__.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/cli.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/cli_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/container.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/container_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/fixtures.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/fixtures_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/harness.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/harness_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/scenarios.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/scenarios_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/suites.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/evals/trial.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/hooks.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/integration_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/main.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/main_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/paths.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/paths_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/persistence.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/prompts/hooks/60-testing.md +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/prompts/taskrunner.md +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/providers.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/providers_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/progress.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/tasks/progress_test.py +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/favicon.js +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/favicon.spec.js +0 -0
- {lemming_cli-0.2.0 → lemming_cli-0.3.0}/src/lemming/web/files.spec.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lemming-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -186,11 +186,17 @@ Lemming comes with several built-in hooks to help manage your project:
|
|
|
186
186
|
analyzes the results of the finished task and decides if the remaining roadmap
|
|
187
187
|
needs to be adjusted (e.g., adding a missing prerequisite, skipping obsolete
|
|
188
188
|
tasks, or breaking down a broad task).
|
|
189
|
-
- **`readability`**: A code quality hook that
|
|
190
|
-
|
|
189
|
+
- **`readability`**: A code quality and simplification hook that challenges
|
|
190
|
+
unnecessary complexity and duplicate implementations, then reviews changes
|
|
191
|
+
for adherence to the Google Style Guide and general readability using the
|
|
191
192
|
[readability](https://github.com/owahltinez/readability) tool (exposed as
|
|
192
193
|
`lemming readability`). It can record findings as task progress or suggest
|
|
193
194
|
follow-up refactoring tasks.
|
|
195
|
+
- **`testing`**: Verifies that changed behavior has focused test coverage and
|
|
196
|
+
that the relevant tests pass.
|
|
197
|
+
- **`ux`**: Reviews at most one critical user journey affected by a
|
|
198
|
+
user-visible change. It reports only concrete, reproducible continuity gaps
|
|
199
|
+
and exits immediately for non-user-facing tasks.
|
|
194
200
|
|
|
195
201
|
### Custom and Global Hooks
|
|
196
202
|
|
|
@@ -240,13 +246,20 @@ See [docs/EVALS.md](docs/EVALS.md) for details.
|
|
|
240
246
|
|
|
241
247
|
### Roadmap Management
|
|
242
248
|
|
|
243
|
-
- **`status [<id>]`**:
|
|
249
|
+
- **`status [<id>]`**: Queue/history overview or deep-dive into a specific
|
|
250
|
+
task, including supersession lineage and runner/orchestrator-hook execution
|
|
251
|
+
times. Superseded and failed history stays visible in the default overview;
|
|
252
|
+
`--verbose` also shows routine completed/cancelled history.
|
|
244
253
|
- **`goal [<text>]`**: Set or view the long-term goal shared by all tasks.
|
|
245
254
|
Supports `-f/--file`.
|
|
246
255
|
- **`add <desc>`**: Append a new task. Supports `--index` and `--runner`.
|
|
247
256
|
- **`edit <id>`**: Modify a task's description, runner, or position.
|
|
248
|
-
- **`delete <id>`**: Remove
|
|
249
|
-
|
|
257
|
+
- **`delete <id>`**: Remove an unstarted task while retaining its runner log.
|
|
258
|
+
Tasks with execution history require `--force`; autonomous restructuring
|
|
259
|
+
should use `supersede`. Supports `--all` and `--completed` for bulk cleanup,
|
|
260
|
+
including logs.
|
|
261
|
+
- **`supersede <id> --reason <text>`**: Retire a replaced or split task without
|
|
262
|
+
losing its progress, timings, log, or links to replacement tasks.
|
|
250
263
|
- **`progress`**: Manage progress entries and findings for specific tasks.
|
|
251
264
|
- `list <id>`: List all progress for a task.
|
|
252
265
|
- `add <id> <finding>`: Record a new technical detail.
|
|
@@ -277,9 +290,11 @@ See [docs/EVALS.md](docs/EVALS.md) for details.
|
|
|
277
290
|
- **`fail <id>`**: Mark a task as a terminal failure (will not be retried).
|
|
278
291
|
- **`cancel <id>`**: Stop an in-progress task (kills the runner process).
|
|
279
292
|
- **`reset <id>`**: Clear attempts and progress to start a task fresh.
|
|
280
|
-
-
|
|
281
|
-
|
|
282
|
-
|
|
293
|
+
- Superseded tasks remain visible as non-failing history; replacement tasks
|
|
294
|
+
link back through their parent task ID.
|
|
295
|
+
- **`logs [<id>]`**: Print a task's execution log to stdout, including retained
|
|
296
|
+
logs for removed tasks. If no ID is provided, it defaults to the active or
|
|
297
|
+
most recent task. Orchestrator hook output is automatically appended.
|
|
283
298
|
|
|
284
299
|
### Execution
|
|
285
300
|
|
|
@@ -307,10 +322,12 @@ Lemming uses **fuzzy matching** to automatically inject the correct "YOLO"
|
|
|
307
322
|
- **Antigravity (`agy`)**: Adds `--dangerously-skip-permissions`
|
|
308
323
|
- **Aider**: Adds `--yes --quiet`
|
|
309
324
|
- **Claude**: Adds `--dangerously-skip-permissions`
|
|
310
|
-
- **Codex**:
|
|
325
|
+
- **Codex**: Runs non-interactively via `codex exec`, adds `--json`, and, in
|
|
326
|
+
YOLO mode, adds `--dangerously-bypass-approvals-and-sandbox`
|
|
311
327
|
|
|
312
|
-
You can disable
|
|
313
|
-
|
|
328
|
+
You can disable default flag injection with `--no-defaults` (`codex exec`
|
|
329
|
+
remains the Codex execution interface), or use a **template** to fully control
|
|
330
|
+
the command layout:
|
|
314
331
|
|
|
315
332
|
```bash
|
|
316
333
|
lemming run --runner "my-tool --input={{prompt}} --json"
|
|
@@ -164,11 +164,17 @@ Lemming comes with several built-in hooks to help manage your project:
|
|
|
164
164
|
analyzes the results of the finished task and decides if the remaining roadmap
|
|
165
165
|
needs to be adjusted (e.g., adding a missing prerequisite, skipping obsolete
|
|
166
166
|
tasks, or breaking down a broad task).
|
|
167
|
-
- **`readability`**: A code quality hook that
|
|
168
|
-
|
|
167
|
+
- **`readability`**: A code quality and simplification hook that challenges
|
|
168
|
+
unnecessary complexity and duplicate implementations, then reviews changes
|
|
169
|
+
for adherence to the Google Style Guide and general readability using the
|
|
169
170
|
[readability](https://github.com/owahltinez/readability) tool (exposed as
|
|
170
171
|
`lemming readability`). It can record findings as task progress or suggest
|
|
171
172
|
follow-up refactoring tasks.
|
|
173
|
+
- **`testing`**: Verifies that changed behavior has focused test coverage and
|
|
174
|
+
that the relevant tests pass.
|
|
175
|
+
- **`ux`**: Reviews at most one critical user journey affected by a
|
|
176
|
+
user-visible change. It reports only concrete, reproducible continuity gaps
|
|
177
|
+
and exits immediately for non-user-facing tasks.
|
|
172
178
|
|
|
173
179
|
### Custom and Global Hooks
|
|
174
180
|
|
|
@@ -218,13 +224,20 @@ See [docs/EVALS.md](docs/EVALS.md) for details.
|
|
|
218
224
|
|
|
219
225
|
### Roadmap Management
|
|
220
226
|
|
|
221
|
-
- **`status [<id>]`**:
|
|
227
|
+
- **`status [<id>]`**: Queue/history overview or deep-dive into a specific
|
|
228
|
+
task, including supersession lineage and runner/orchestrator-hook execution
|
|
229
|
+
times. Superseded and failed history stays visible in the default overview;
|
|
230
|
+
`--verbose` also shows routine completed/cancelled history.
|
|
222
231
|
- **`goal [<text>]`**: Set or view the long-term goal shared by all tasks.
|
|
223
232
|
Supports `-f/--file`.
|
|
224
233
|
- **`add <desc>`**: Append a new task. Supports `--index` and `--runner`.
|
|
225
234
|
- **`edit <id>`**: Modify a task's description, runner, or position.
|
|
226
|
-
- **`delete <id>`**: Remove
|
|
227
|
-
|
|
235
|
+
- **`delete <id>`**: Remove an unstarted task while retaining its runner log.
|
|
236
|
+
Tasks with execution history require `--force`; autonomous restructuring
|
|
237
|
+
should use `supersede`. Supports `--all` and `--completed` for bulk cleanup,
|
|
238
|
+
including logs.
|
|
239
|
+
- **`supersede <id> --reason <text>`**: Retire a replaced or split task without
|
|
240
|
+
losing its progress, timings, log, or links to replacement tasks.
|
|
228
241
|
- **`progress`**: Manage progress entries and findings for specific tasks.
|
|
229
242
|
- `list <id>`: List all progress for a task.
|
|
230
243
|
- `add <id> <finding>`: Record a new technical detail.
|
|
@@ -255,9 +268,11 @@ See [docs/EVALS.md](docs/EVALS.md) for details.
|
|
|
255
268
|
- **`fail <id>`**: Mark a task as a terminal failure (will not be retried).
|
|
256
269
|
- **`cancel <id>`**: Stop an in-progress task (kills the runner process).
|
|
257
270
|
- **`reset <id>`**: Clear attempts and progress to start a task fresh.
|
|
258
|
-
-
|
|
259
|
-
|
|
260
|
-
|
|
271
|
+
- Superseded tasks remain visible as non-failing history; replacement tasks
|
|
272
|
+
link back through their parent task ID.
|
|
273
|
+
- **`logs [<id>]`**: Print a task's execution log to stdout, including retained
|
|
274
|
+
logs for removed tasks. If no ID is provided, it defaults to the active or
|
|
275
|
+
most recent task. Orchestrator hook output is automatically appended.
|
|
261
276
|
|
|
262
277
|
### Execution
|
|
263
278
|
|
|
@@ -285,10 +300,12 @@ Lemming uses **fuzzy matching** to automatically inject the correct "YOLO"
|
|
|
285
300
|
- **Antigravity (`agy`)**: Adds `--dangerously-skip-permissions`
|
|
286
301
|
- **Aider**: Adds `--yes --quiet`
|
|
287
302
|
- **Claude**: Adds `--dangerously-skip-permissions`
|
|
288
|
-
- **Codex**:
|
|
303
|
+
- **Codex**: Runs non-interactively via `codex exec`, adds `--json`, and, in
|
|
304
|
+
YOLO mode, adds `--dangerously-bypass-approvals-and-sandbox`
|
|
289
305
|
|
|
290
|
-
You can disable
|
|
291
|
-
|
|
306
|
+
You can disable default flag injection with `--no-defaults` (`codex exec`
|
|
307
|
+
remains the Codex execution interface), or use a **template** to fully control
|
|
308
|
+
the command layout:
|
|
292
309
|
|
|
293
310
|
```bash
|
|
294
311
|
lemming run --runner "my-tool --input={{prompt}} --json"
|
|
@@ -51,15 +51,30 @@ a broad task).
|
|
|
51
51
|
|
|
52
52
|
### `readability`
|
|
53
53
|
|
|
54
|
-
A code quality hook that
|
|
55
|
-
and
|
|
56
|
-
|
|
54
|
+
A code quality and simplification hook that challenges unnecessary complexity,
|
|
55
|
+
indirection, and duplicated implementations before reviewing changes for
|
|
56
|
+
adherence to the Google Style Guide and general readability using the bundled
|
|
57
|
+
`lemming readability` tool. It provides feedback via task progress or suggests
|
|
58
|
+
follow-up refactoring tasks.
|
|
59
|
+
|
|
60
|
+
### `testing`
|
|
61
|
+
|
|
62
|
+
Checks that changed behavior has focused test coverage and runs the relevant
|
|
63
|
+
tests. It records larger testing gaps as task progress for the roadmap hook.
|
|
64
|
+
|
|
65
|
+
### `ux`
|
|
66
|
+
|
|
67
|
+
A lightweight journey-continuity review for user-visible changes. It considers
|
|
68
|
+
at most one affected critical journey, reports only concrete and reproducible
|
|
69
|
+
gaps, and exits immediately when the task did not change user-facing behavior.
|
|
70
|
+
It runs after `testing` and before `roadmap`.
|
|
57
71
|
|
|
58
72
|
### Editing Built-in Hook Prompts
|
|
59
73
|
|
|
60
74
|
The built-in hook prompts are load-bearing: a wording change can regress
|
|
61
75
|
orchestration behavior without any unit test failing. When you edit a prompt
|
|
62
|
-
under `src/lemming/prompts/hooks
|
|
76
|
+
under `src/lemming/prompts/hooks/` that has an eval suite, run it before
|
|
77
|
+
shipping:
|
|
63
78
|
|
|
64
79
|
```bash
|
|
65
80
|
uv run python -m lemming.evals run --suite roadmap
|
|
Binary file
|
|
Binary file
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@biomejs/biome": "1.9.4",
|
|
13
13
|
"@playwright/test": "^1.60.0",
|
|
14
14
|
"jsdom": "latest",
|
|
15
|
-
"mancha": "^0.22.
|
|
15
|
+
"mancha": "^0.22.7",
|
|
16
16
|
"playwright": "^1.60.0"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
@@ -1019,9 +1019,9 @@
|
|
|
1019
1019
|
}
|
|
1020
1020
|
},
|
|
1021
1021
|
"node_modules/mancha": {
|
|
1022
|
-
"version": "0.22.
|
|
1023
|
-
"resolved": "https://registry.npmjs.org/mancha/-/mancha-0.22.
|
|
1024
|
-
"integrity": "sha512-
|
|
1022
|
+
"version": "0.22.7",
|
|
1023
|
+
"resolved": "https://registry.npmjs.org/mancha/-/mancha-0.22.7.tgz",
|
|
1024
|
+
"integrity": "sha512-ZClxBTYOKSD9f4KqW8lCMYVEUPhxzIRgihw5g20QrD4kzZ9JcQ383LXXkPMVTaCAjLlqYaF3brUMYACwWgQZgA==",
|
|
1025
1025
|
"dev": true,
|
|
1026
1026
|
"license": "MIT",
|
|
1027
1027
|
"dependencies": {
|
|
@@ -15,6 +15,9 @@ def test_list_hooks(client, test_tasks, monkeypatch, tmp_path):
|
|
|
15
15
|
assert hooks["roadmap"]["masked"] is False
|
|
16
16
|
assert hooks["readability"]["source"] == "built-in"
|
|
17
17
|
assert hooks["readability"]["runs_on_failure"] is False
|
|
18
|
+
assert hooks["ux"]["priority"] == 70
|
|
19
|
+
assert hooks["ux"]["source"] == "built-in"
|
|
20
|
+
assert hooks["ux"]["runs_on_failure"] is False
|
|
18
21
|
|
|
19
22
|
# Hooks are sorted by ascending priority
|
|
20
23
|
priorities = [h["priority"] for h in payload]
|
|
@@ -76,6 +76,11 @@ def update_task(
|
|
|
76
76
|
"""Update a task's description, runner, position, status, or parent."""
|
|
77
77
|
tasks_file = context.resolve_tasks_file(request.app.state, project)
|
|
78
78
|
status = update.get("status")
|
|
79
|
+
if status == tasks.TaskStatus.SUPERSEDED:
|
|
80
|
+
raise fastapi.HTTPException(
|
|
81
|
+
400,
|
|
82
|
+
"Use the supersede endpoint so a reason is recorded.",
|
|
83
|
+
)
|
|
79
84
|
|
|
80
85
|
# Validation: require progress if completing or failing from the UI,
|
|
81
86
|
# but not if we are just marking a finished task as pending (uncomplete).
|
|
@@ -91,6 +96,7 @@ def update_task(
|
|
|
91
96
|
tasks.TaskStatus.COMPLETED,
|
|
92
97
|
tasks.TaskStatus.FAILED,
|
|
93
98
|
tasks.TaskStatus.CANCELLED,
|
|
99
|
+
tasks.TaskStatus.SUPERSEDED,
|
|
94
100
|
):
|
|
95
101
|
require_progress = True
|
|
96
102
|
|
|
@@ -130,11 +136,39 @@ def delete_task(
|
|
|
130
136
|
):
|
|
131
137
|
"""Delete a single task by ID."""
|
|
132
138
|
tasks.delete_tasks(
|
|
133
|
-
context.resolve_tasks_file(request.app.state, project),
|
|
139
|
+
context.resolve_tasks_file(request.app.state, project),
|
|
140
|
+
task_id=task_id,
|
|
141
|
+
force=True,
|
|
134
142
|
)
|
|
135
143
|
return {"status": "ok"}
|
|
136
144
|
|
|
137
145
|
|
|
146
|
+
class SupersedeTaskRequest(pydantic.BaseModel):
|
|
147
|
+
"""Request body for superseding a task."""
|
|
148
|
+
|
|
149
|
+
reason: str
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@router.post("/api/tasks/{task_id}/supersede")
|
|
153
|
+
def supersede_task(
|
|
154
|
+
request: fastapi.Request,
|
|
155
|
+
task_id: str,
|
|
156
|
+
body: SupersedeTaskRequest,
|
|
157
|
+
project: str | None = None,
|
|
158
|
+
):
|
|
159
|
+
"""Supersede a task while retaining its history."""
|
|
160
|
+
try:
|
|
161
|
+
return tasks.supersede_task(
|
|
162
|
+
context.resolve_tasks_file(request.app.state, project),
|
|
163
|
+
task_id,
|
|
164
|
+
body.reason,
|
|
165
|
+
)
|
|
166
|
+
except ValueError as e:
|
|
167
|
+
if "not found" in str(e):
|
|
168
|
+
raise fastapi.HTTPException(404, str(e))
|
|
169
|
+
raise fastapi.HTTPException(400, str(e))
|
|
170
|
+
|
|
171
|
+
|
|
138
172
|
@router.post("/api/tasks/{task_id}/cancel")
|
|
139
173
|
def cancel_task_endpoint(
|
|
140
174
|
request: fastapi.Request, task_id: str, project: str | None = None
|
|
@@ -174,6 +174,25 @@ def test_reopen_cancelled_task_via_api(client, test_tasks):
|
|
|
174
174
|
assert response.json()["attempts"] == 0
|
|
175
175
|
|
|
176
176
|
|
|
177
|
+
def test_supersede_and_reopen_task_via_api(client, test_tasks):
|
|
178
|
+
response = client.post(
|
|
179
|
+
"/api/tasks/task2/supersede",
|
|
180
|
+
json={"reason": "split after timeout"},
|
|
181
|
+
)
|
|
182
|
+
assert response.status_code == 200
|
|
183
|
+
assert response.json()["status"] == tasks.TaskStatus.SUPERSEDED
|
|
184
|
+
assert response.json()["superseded_reason"] == "split after timeout"
|
|
185
|
+
|
|
186
|
+
response = client.post(
|
|
187
|
+
"/api/tasks/task2/update",
|
|
188
|
+
json={"status": tasks.TaskStatus.PENDING},
|
|
189
|
+
)
|
|
190
|
+
assert response.status_code == 200
|
|
191
|
+
assert response.json()["status"] == tasks.TaskStatus.PENDING
|
|
192
|
+
assert response.json()["superseded_reason"] is None
|
|
193
|
+
assert response.json()["superseded_at"] is None
|
|
194
|
+
|
|
195
|
+
|
|
177
196
|
def test_has_log_population(client, test_tasks):
|
|
178
197
|
# Initially no logs
|
|
179
198
|
response = client.get("/api/data")
|
|
@@ -225,7 +244,7 @@ def test_api_log(client, test_tasks):
|
|
|
225
244
|
assert response.json() == {"log": ""}
|
|
226
245
|
|
|
227
246
|
|
|
228
|
-
def
|
|
247
|
+
def test_api_delete_retains_log(client, test_tasks):
|
|
229
248
|
test_tasks_file = test_tasks
|
|
230
249
|
# 1. Add a task
|
|
231
250
|
data = tasks.load_tasks(test_tasks_file)
|
|
@@ -248,6 +267,20 @@ def test_api_delete_log_cleanup(client, test_tasks):
|
|
|
248
267
|
|
|
249
268
|
# 3. Delete via API
|
|
250
269
|
response = client.post(f"/api/tasks/{task_id}/delete")
|
|
270
|
+
assert response.status_code == 200
|
|
271
|
+
assert log_file.read_text() == "API delete log"
|
|
272
|
+
|
|
273
|
+
response = client.get(f"/api/tasks/{task_id}/log")
|
|
274
|
+
assert response.status_code == 200
|
|
275
|
+
assert response.json() == {"log": "API delete log"}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_api_delete_completed_cleans_up_log(client, test_tasks):
|
|
279
|
+
log_file = paths.get_log_file(test_tasks, "task1")
|
|
280
|
+
log_file.write_text("completed task log")
|
|
281
|
+
|
|
282
|
+
response = client.post("/api/tasks/delete-completed")
|
|
283
|
+
|
|
251
284
|
assert response.status_code == 200
|
|
252
285
|
assert not log_file.exists()
|
|
253
286
|
|