lemming-cli 0.3.0__tar.gz → 0.3.2__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.3.0 → lemming_cli-0.3.2}/PKG-INFO +1 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/HOOKS.md +7 -1
- lemming_cli-0.3.2/docs/screenshots/dashboard-desktop.png +0 -0
- lemming_cli-0.3.2/docs/screenshots/dashboard-mobile.png +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/pyproject.toml +1 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/tasks_test.py +13 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/progress_test.py +20 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/tasks.py +2 -2
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/tasks_test.py +12 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/models.py +2 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/models_test.py +2 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/orchestrator.py +7 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/orchestrator_test.py +35 -2
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/90-roadmap.md +14 -9
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/taskrunner.md +21 -21
- lemming_cli-0.3.2/src/lemming/prompts.py +525 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts_test.py +106 -3
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/runner.py +13 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/runner_test.py +49 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/__init__.py +9 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/lifecycle.py +42 -11
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/lifecycle_test.py +80 -13
- lemming_cli-0.3.2/src/lemming/tasks/limits.py +45 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/operations.py +12 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/operations_test.py +67 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/progress.py +2 -0
- lemming_cli-0.3.2/src/lemming/tasks/progress_test.py +54 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/dashboard.spec.js +37 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/dashboard.test.js +46 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/index.js +15 -1
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/uv.lock +1 -1
- lemming_cli-0.3.0/docs/screenshots/dashboard-desktop.png +0 -0
- lemming_cli-0.3.0/docs/screenshots/dashboard-mobile.png +0 -0
- lemming_cli-0.3.0/src/lemming/prompts.py +0 -267
- lemming_cli-0.3.0/src/lemming/tasks/progress_test.py +0 -22
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.github/workflows/ci.yml +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.github/workflows/publish.yml +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.github/workflows/screenshots.yml +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.gitignore +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.prettierignore +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.prettierrc +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/Dockerfile +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/LICENSE +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/README.md +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/biome.json +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docker-compose.yml +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/EVALS.md +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/screenshots/task-log-desktop.png +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/screenshots/task-log-mobile.png +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/package-lock.json +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/package.json +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/playwright.config.js +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/__init__.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/__init__.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/auth.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/auth_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/config.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/config_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/conftest.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/context.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/context_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/directories.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/directories_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/files.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/files_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/hooks.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/hooks_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/logging.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/logging_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/loop.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/loop_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/main.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/main_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/tasks.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/__init__.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/config.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/config_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/goal.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/goal_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/hooks.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/hooks_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/main.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/main_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/operations.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/operations_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/progress.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/readability_cli.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/readability_cli_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/conftest.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/__init__.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/__main__.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/cli.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/cli_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/container.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/container_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/fixtures.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/fixtures_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/harness.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/harness_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/readability.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/readability_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/roadmap.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/roadmap_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/scenarios.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/scenarios_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/suites.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/trial.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/trial_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/hooks.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/hooks_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/integration_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/main.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/main_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/paths.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/paths_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/persistence.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/persistence_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/50-readability.md +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/60-testing.md +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/70-ux.md +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/providers.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/providers_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/queries.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/queries_test.py +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/favicon.js +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/favicon.spec.js +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/files.html +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/files.spec.js +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/index.html +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/logs.html +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/logs.test.js +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/mancha.js +0 -0
- {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/screenshots.spec.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lemming-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
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
|
|
@@ -155,11 +155,17 @@ Your hook template can use the following placeholders:
|
|
|
155
155
|
|
|
156
156
|
- `{{roadmap}}`: A structured summary of the long-term goal and all tasks.
|
|
157
157
|
- `{{finished_task}}`: Details about the task that just finished (ID,
|
|
158
|
-
description, progress, and
|
|
158
|
+
description, recent progress, and a recent execution-log excerpt capped at
|
|
159
|
+
16 KiB).
|
|
159
160
|
- `{{finished_task_id}}`: The ID of the task that just finished.
|
|
160
161
|
- `{{tasks_file_name}}`: The filename of the tasks YAML file.
|
|
161
162
|
- `{{tasks_file_path}}`: The full path to the tasks YAML file.
|
|
162
163
|
|
|
164
|
+
Roadmap context is size-bounded and prioritizes current and actionable tasks
|
|
165
|
+
over older completed history. Readability, testing, and UX hooks receive a
|
|
166
|
+
smaller roadmap summary because their primary context is the finished task and
|
|
167
|
+
the workspace.
|
|
168
|
+
|
|
163
169
|
## Developer Ergonomics
|
|
164
170
|
|
|
165
171
|
### Listing Hooks
|
|
Binary file
|
|
Binary file
|
|
@@ -30,6 +30,19 @@ def test_add_task(client, test_tasks):
|
|
|
30
30
|
assert any(t.description == "New task from test" for t in roadmap.tasks)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def test_add_task_rejects_oversized_description(client, test_tasks):
|
|
34
|
+
response = client.post(
|
|
35
|
+
"/api/tasks",
|
|
36
|
+
json={
|
|
37
|
+
"description": "x" * (tasks.MAX_TASK_DESCRIPTION_CHARS + 1),
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
assert response.status_code == 400
|
|
42
|
+
assert "2,001 characters (limit 2,000)" in response.json()["detail"]
|
|
43
|
+
assert len(tasks.load_tasks(test_tasks).tasks) == 3
|
|
44
|
+
|
|
45
|
+
|
|
33
46
|
def test_add_task_with_runner(client, test_tasks):
|
|
34
47
|
response = client.post(
|
|
35
48
|
"/api/tasks", json={"description": "Runner task", "runner": "claude"}
|
|
@@ -38,3 +38,23 @@ def test_progress(setup_env):
|
|
|
38
38
|
|
|
39
39
|
data = tasks.load_tasks(tasks_file)
|
|
40
40
|
assert "Observed behavior X" in data.tasks[0].progress
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_progress_reports_actionable_size_error(setup_env):
|
|
44
|
+
runner, base_args, tasks_file = setup_env
|
|
45
|
+
|
|
46
|
+
result = runner.invoke(
|
|
47
|
+
cli.cli,
|
|
48
|
+
base_args
|
|
49
|
+
+ [
|
|
50
|
+
"progress",
|
|
51
|
+
"12345678",
|
|
52
|
+
"x" * (tasks.MAX_PROGRESS_ENTRY_CHARS + 1),
|
|
53
|
+
],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
assert result.exit_code == 1
|
|
57
|
+
assert "281 characters (limit 280)" in result.output
|
|
58
|
+
assert "Write detailed evidence or verbose command output" in result.output
|
|
59
|
+
assert str(tasks_file.parent) not in result.output
|
|
60
|
+
assert tasks.load_tasks(tasks_file).tasks[0].progress == []
|
|
@@ -645,11 +645,11 @@ def fail(ctx: click.Context, task_id: str):
|
|
|
645
645
|
ctx.exit(1)
|
|
646
646
|
|
|
647
647
|
|
|
648
|
-
@cli.command(short_help="<taskid>
|
|
648
|
+
@cli.command(short_help="<taskid> Cancel a pending or active task")
|
|
649
649
|
@click.argument("task_id")
|
|
650
650
|
@click.pass_context
|
|
651
651
|
def cancel(ctx: click.Context, task_id: str):
|
|
652
|
-
"""
|
|
652
|
+
"""Cancels a task, stopping its runner if it is active."""
|
|
653
653
|
tasks_file = ctx.obj["TASKS_FILE"]
|
|
654
654
|
if tasks.cancel_task(tasks_file, task_id):
|
|
655
655
|
click.echo(f"Task {task_id} cancelled.")
|
|
@@ -51,6 +51,18 @@ class TestCLITasks(unittest.TestCase):
|
|
|
51
51
|
task_descs = [t.description for t in data.tasks]
|
|
52
52
|
self.assertIn("New Task", task_descs)
|
|
53
53
|
|
|
54
|
+
def test_add_task_reports_actionable_size_error(self):
|
|
55
|
+
result = self.cli_runner.invoke(
|
|
56
|
+
cli.cli,
|
|
57
|
+
self.base_args
|
|
58
|
+
+ ["add", "x" * (tasks.MAX_TASK_DESCRIPTION_CHARS + 1)],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
self.assertEqual(result.exit_code, 1)
|
|
62
|
+
self.assertIn("2,001 characters (limit 2,000)", result.output)
|
|
63
|
+
self.assertIn("Keep the brief task-specific", result.output)
|
|
64
|
+
self.assertEqual(len(tasks.load_tasks(self.test_tasks_file).tasks), 1)
|
|
65
|
+
|
|
54
66
|
def test_edit_task_description(self):
|
|
55
67
|
result = self.cli_runner.invoke(
|
|
56
68
|
cli.cli,
|
|
@@ -60,6 +60,8 @@ class Task(pydantic.BaseModel):
|
|
|
60
60
|
created_at: float = pydantic.Field(default_factory=time.time)
|
|
61
61
|
run_time: float = 0.0
|
|
62
62
|
execution_times: dict[str, float] | None = None
|
|
63
|
+
active_execution_component: str | None = None
|
|
64
|
+
active_execution_started_at: float | None = None
|
|
63
65
|
pid: int | None = None
|
|
64
66
|
last_heartbeat: float | None = None
|
|
65
67
|
has_runner_log: bool = False
|
|
@@ -19,6 +19,8 @@ def test_task_model_defaults():
|
|
|
19
19
|
assert task.progress == []
|
|
20
20
|
assert task.run_time == 0.0
|
|
21
21
|
assert task.execution_times is None
|
|
22
|
+
assert task.active_execution_component is None
|
|
23
|
+
assert task.active_execution_started_at is None
|
|
22
24
|
assert task.superseded_at is None
|
|
23
25
|
assert task.superseded_reason is None
|
|
24
26
|
assert task.created_at > 0
|
|
@@ -357,8 +357,14 @@ def _handle_runner_exit(
|
|
|
357
357
|
click.echo(stderr, err=True)
|
|
358
358
|
|
|
359
359
|
if returncode == -15:
|
|
360
|
+
if post_task.status == tasks.TaskStatus.CANCELLED:
|
|
361
|
+
if verbose:
|
|
362
|
+
click.echo("Task was cancelled. Continuing orchestrator.")
|
|
363
|
+
return False
|
|
360
364
|
if verbose:
|
|
361
|
-
click.echo(
|
|
365
|
+
click.echo(
|
|
366
|
+
"Runner exited after SIGTERM. Stopping orchestrator loop."
|
|
367
|
+
)
|
|
362
368
|
return True
|
|
363
369
|
|
|
364
370
|
if runner_failed and not post_task.requested_status:
|
|
@@ -483,7 +483,9 @@ def test_handle_runner_exit_completes(mock_sleep, mock_run_hooks, setup_env):
|
|
|
483
483
|
|
|
484
484
|
|
|
485
485
|
@mock.patch("lemming.orchestrator.run_hooks")
|
|
486
|
-
def
|
|
486
|
+
def test_handle_runner_exit_stops_on_unexpected_sigterm(
|
|
487
|
+
mock_run_hooks, setup_env
|
|
488
|
+
):
|
|
487
489
|
test_tasks_file, initial_data = setup_env
|
|
488
490
|
data = tasks.load_tasks(test_tasks_file)
|
|
489
491
|
task = data.tasks[0]
|
|
@@ -507,7 +509,38 @@ def test_handle_runner_exit_cancelled(mock_run_hooks, setup_env):
|
|
|
507
509
|
working_dir=None,
|
|
508
510
|
time_limit=1,
|
|
509
511
|
)
|
|
510
|
-
assert should_abort
|
|
512
|
+
assert should_abort
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
@mock.patch("lemming.orchestrator.run_hooks")
|
|
516
|
+
def test_handle_runner_exit_continues_after_cancellation(
|
|
517
|
+
mock_run_hooks, setup_env
|
|
518
|
+
):
|
|
519
|
+
test_tasks_file, initial_data = setup_env
|
|
520
|
+
data = tasks.load_tasks(test_tasks_file)
|
|
521
|
+
task = data.tasks[0]
|
|
522
|
+
task.status = tasks.TaskStatus.CANCELLED
|
|
523
|
+
tasks.save_tasks(test_tasks_file, data)
|
|
524
|
+
|
|
525
|
+
should_abort = _handle_runner_exit(
|
|
526
|
+
test_tasks_file,
|
|
527
|
+
task.id,
|
|
528
|
+
returncode=-15,
|
|
529
|
+
stdout="",
|
|
530
|
+
stderr="",
|
|
531
|
+
retries=3,
|
|
532
|
+
retry_delay=5,
|
|
533
|
+
runner_name="agy",
|
|
534
|
+
yolo=True,
|
|
535
|
+
runner_args=(),
|
|
536
|
+
no_defaults=False,
|
|
537
|
+
verbose=False,
|
|
538
|
+
active_hooks=[],
|
|
539
|
+
working_dir=None,
|
|
540
|
+
time_limit=1,
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
assert not should_abort
|
|
511
544
|
|
|
512
545
|
|
|
513
546
|
@mock.patch("lemming.orchestrator.time.sleep")
|
|
@@ -49,16 +49,21 @@ the plan up-to-date with minimal friction.
|
|
|
49
49
|
forgotten teardowns, missing tests reported by the testing hook, or
|
|
50
50
|
formatting issues reported by the readability hook), add new tasks to
|
|
51
51
|
address them.
|
|
52
|
-
7. **
|
|
52
|
+
7. **Task-Specific Briefs**: Keep new or rewritten task descriptions concise,
|
|
53
|
+
self-contained, and no more than {{max_task_description_chars}} characters.
|
|
54
|
+
Include the concrete files, symbols, motivation, and acceptance criteria the
|
|
55
|
+
task needs, but do not repeat project-wide rules already present in the
|
|
56
|
+
long-term goal.
|
|
57
|
+
8. **No Code Changes**: Your only persistent changes may be to the roadmap via
|
|
53
58
|
the `lemming` CLI. Do NOT edit source or configuration files. Reading the
|
|
54
|
-
workspace and running existing build, test, and entry-point commands for
|
|
55
|
-
queue-drain audit is explicitly allowed.
|
|
56
|
-
|
|
57
|
-
no failed tasks that have reached their maximum attempts, you may exit
|
|
58
|
-
immediately without running any commands. The queue-drain audit in
|
|
59
|
-
4 must finish before this clause applies. If a task is marked as
|
|
60
|
-
has reached its maximum attempts, a Fast Exit will result in the
|
|
61
|
-
project ABORTING. In that case, you MUST repair it.
|
|
59
|
+
workspace and running existing build, test, and entry-point commands for
|
|
60
|
+
the queue-drain audit is explicitly allowed.
|
|
61
|
+
9. **Fast Exit**: If the roadmap is accurate and well-structured, AND there
|
|
62
|
+
are no failed tasks that have reached their maximum attempts, you may exit
|
|
63
|
+
immediately without running any commands. The queue-drain audit in
|
|
64
|
+
directive 4 must finish before this clause applies. If a task is marked as
|
|
65
|
+
FAILED and has reached its maximum attempts, a Fast Exit will result in the
|
|
66
|
+
entire project ABORTING. In that case, you MUST repair it.
|
|
62
67
|
|
|
63
68
|
## Commands
|
|
64
69
|
|
|
@@ -36,33 +36,33 @@ Your CURRENT, EXCLUSIVE task is: **{{description}}**
|
|
|
36
36
|
a fresh, empty conversation history. Their only context is the global
|
|
37
37
|
roadmap, previously recorded progress from completed tasks, and the state
|
|
38
38
|
of the file system.
|
|
39
|
-
- **
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
- **Task-Specific Descriptions:** New task descriptions must be concise,
|
|
40
|
+
self-contained, and no more than {{max_task_description_chars}} characters.
|
|
41
|
+
Include the file paths, symbols, motivation, and acceptance criteria that
|
|
42
|
+
task needs, but do not restate project-wide rules already present in the
|
|
43
|
+
long-term goal.
|
|
44
|
+
- **Detailed Evidence:** Keep verbose gate output, transcripts, error traces,
|
|
45
|
+
and other ephemeral evidence in `{{tasks_dir}}`, which is Lemming's
|
|
46
|
+
per-project scratch directory outside the workspace. Reference the file
|
|
47
|
+
from a task description or progress entry when another attempt needs it.
|
|
48
|
+
Put conclusions that must outlive the task in the commit message or a
|
|
49
|
+
decision document in the repository.
|
|
50
50
|
|
|
51
|
-
4. **Progress:** Your first action should be to record a
|
|
52
|
-
describing
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
4. **Progress:** Your first action should be to record a one-line progress entry
|
|
52
|
+
describing your approach. Continue recording concise findings as you work,
|
|
53
|
+
but keep each entry to one line and no more than
|
|
54
|
+
{{max_progress_entry_chars}} characters. Do not paste command output or
|
|
55
|
+
detailed evidence into progress; write it under `{{tasks_dir}}` and reference
|
|
56
|
+
the file instead. If your process is killed, recorded progress carries over
|
|
57
|
+
to the next attempt. At least one progress entry is required before
|
|
58
58
|
completing or failing a task:
|
|
59
59
|
`lemming --tasks-file {{tasks_file_path}} progress {{task_id}} '<what you did or found>'`
|
|
60
60
|
5. **Success:** When you have completely finished and verified the task, and
|
|
61
|
-
recorded
|
|
61
|
+
recorded relevant progress (at least one entry is required), run:
|
|
62
62
|
`lemming --tasks-file {{tasks_file_path}} complete {{task_id}}`
|
|
63
63
|
6. **Failure/Blocker:** If you hit a technical roadblock, cannot fix a bug, or
|
|
64
|
-
are unable to complete the task, after recording
|
|
65
|
-
|
|
64
|
+
are unable to complete the task, after recording relevant progress (at least
|
|
65
|
+
one entry is required), run:
|
|
66
66
|
`lemming --tasks-file {{tasks_file_path}} fail {{task_id}}`
|
|
67
67
|
|
|
68
68
|
7. Stop and exit after running either the complete or fail command.
|