lemming-cli 0.5.2__tar.gz → 0.6.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.5.2/README.md → lemming_cli-0.6.0/PKG-INFO +51 -7
- lemming_cli-0.5.2/PKG-INFO → lemming_cli-0.6.0/README.md +28 -30
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/SKILL.md +7 -1
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/docs/HOOKS.md +32 -0
- lemming_cli-0.6.0/docs/WORKSPACE.md +120 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/pyproject.toml +3 -3
- lemming_cli-0.6.0/src/lemming/api/__init__.py +1 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/auth_test.py +14 -14
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/config.py +7 -6
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/config_test.py +3 -3
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/conftest.py +43 -42
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/directories_test.py +5 -5
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/hooks_test.py +2 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/logging_test.py +2 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/loop.py +2 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/loop_test.py +9 -3
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/main.py +0 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/tasks.py +22 -21
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/tasks_test.py +76 -60
- lemming_cli-0.6.0/src/lemming/artifact_test.py +141 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/brief_test.py +6 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/__init__.py +1 -3
- lemming_cli-0.6.0/src/lemming/cli/artifact.py +141 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/brief.py +5 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/config.py +4 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/config_test.py +9 -8
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/exec_cli.py +16 -13
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/exec_cli_test.py +71 -58
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/goal.py +5 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/goal_test.py +5 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/hooks_test.py +4 -3
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/machine_output_test.py +8 -7
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/main_test.py +26 -20
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/operations.py +43 -27
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/operations_test.py +24 -11
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/progress.py +2 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/progress_test.py +10 -9
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/skill_test.py +3 -3
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/stop_test.py +18 -17
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/tasks.py +99 -55
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/tasks_test.py +73 -56
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/fixtures.py +3 -3
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/harness_test.py +3 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/readability_test.py +8 -7
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/roadmap_test.py +20 -17
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/trial.py +7 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/integration_test.py +40 -51
- lemming_cli-0.6.0/src/lemming/main.py +18 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/main_test.py +18 -5
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/models.py +5 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/orchestrator.py +105 -50
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/orchestrator_test.py +261 -136
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/paths.py +52 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/prompts/hooks/50-readability.md +11 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/prompts/hooks/60-testing.md +13 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/prompts/hooks/90-roadmap.md +18 -11
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/prompts/taskrunner.md +39 -14
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/prompts.py +78 -27
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/prompts_test.py +43 -43
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/runner.py +9 -8
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/runner_test.py +54 -47
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/shutdown_integration_test.py +7 -7
- lemming_cli-0.6.0/src/lemming/tasks/__init__.py +1 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/lifecycle.py +57 -4
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/lifecycle_test.py +29 -1
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/limits.py +5 -10
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/operations.py +31 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/progress.py +3 -5
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/progress_test.py +2 -2
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/uv.lock +5 -5
- lemming_cli-0.5.2/src/lemming/api/__init__.py +0 -5
- lemming_cli-0.5.2/src/lemming/api_test.py +0 -7
- lemming_cli-0.5.2/src/lemming/cli_test.py +0 -22
- lemming_cli-0.5.2/src/lemming/main.py +0 -6
- lemming_cli-0.5.2/src/lemming/tasks/__init__.py +0 -108
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/.dockerignore +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/.github/workflows/ci.yml +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/.github/workflows/publish.yml +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/.github/workflows/screenshots.yml +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/.gitignore +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/.prettierignore +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/.prettierrc +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/Dockerfile +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/LICENSE +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/biome.json +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/docker-compose.yml +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/docs/EVALS.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/docs/screenshots/dashboard-desktop.png +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/docs/screenshots/dashboard-mobile.png +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/docs/screenshots/task-log-desktop.png +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/docs/screenshots/task-log-mobile.png +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/package-lock.json +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/package.json +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/playwright.config.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/auth.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/context.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/context_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/directories.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/files.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/files_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/hooks.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/logging.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/api/main_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/hooks.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/main.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/readability_cli.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/readability_cli_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/cli/skill.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/conftest.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/__main__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/cli.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/cli_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/container.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/container_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/fixtures_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/harness.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/metrics.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/metrics_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/base/README.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/base/calc/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/base/calc/legacy.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/base/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/base/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/clean/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/clean/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/dead-code/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/dead-code/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/duplicated-behavior/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/duplicated-behavior/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/false-reuse/calc/limits.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/false-reuse/calc/limits_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/lint-debt/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/readability/lint-debt/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/add-only/README.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/add-only/calc/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/add-only/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/add-only/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/buggy-subtract/README.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/buggy-subtract/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/full-ops/README.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/full-ops/calc/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/full-ops/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/full-ops/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/unwired-cli/README.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/cli.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/cli_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/ops.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/roadmap/unwired-cli/calc/ops_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/task/hidden/stats/summary_hidden_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/task/summary/README.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/task/summary/stats/__init__.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/task/summary/stats/summary.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/projects/task/summary/stats/summary_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/readability.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/report.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/report_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/roadmap.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/scenarios.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/scenarios_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/suites.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/task.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/task_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/evals/trial_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/hooks.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/hooks_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/models_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/paths_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/persistence.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/persistence_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/prompts/hooks/70-ux.md +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/providers.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/providers_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/scope.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/scope_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/shutdown.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/shutdown_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/operations_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/queries.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/tasks/queries_test.py +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/dashboard.spec.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/dashboard.test.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/favicon.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/favicon.spec.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/files.html +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/files.spec.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/index.html +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/index.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/logs.html +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/logs.test.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/mancha.js +0 -0
- {lemming_cli-0.5.2 → lemming_cli-0.6.0}/src/lemming/web/screenshots.spec.js +0 -0
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lemming-cli
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: An autonomous, iterative task runner for AI coding agents.
|
|
5
|
+
Project-URL: Homepage, https://github.com/owahltinez/lemming
|
|
6
|
+
Project-URL: Repository, https://github.com/owahltinez/lemming
|
|
7
|
+
Author: Lemming Contributors
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,ai,antigravity,automation,claude,opencode
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Requires-Dist: beautifulsoup4>=4.13.3
|
|
13
|
+
Requires-Dist: click-agentcli>=0.5.0
|
|
14
|
+
Requires-Dist: click>=8.0.0
|
|
15
|
+
Requires-Dist: fastapi>=0.124.4
|
|
16
|
+
Requires-Dist: markdownify>=0.14.1
|
|
17
|
+
Requires-Dist: pydantic>=2.12.5
|
|
18
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
19
|
+
Requires-Dist: readability-cli>=0.10.0
|
|
20
|
+
Requires-Dist: requests>=2.32.3
|
|
21
|
+
Requires-Dist: uvicorn>=0.33.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
1
24
|
# Lemming 🐹
|
|
2
25
|
|
|
3
26
|
**The transparent, tool-agnostic orchestrator for autonomous AI coding agents.**
|
|
@@ -78,6 +101,11 @@ lemming run
|
|
|
78
101
|
lemming run -- --model claude-3-5-sonnet
|
|
79
102
|
```
|
|
80
103
|
|
|
104
|
+
The loop runs every task in one shared workspace, and the orchestrator never
|
|
105
|
+
branches or commits on your behalf. Give the whole run its own branch or
|
|
106
|
+
worktree first if you want to be able to throw it away — see
|
|
107
|
+
[docs/WORKSPACE.md](docs/WORKSPACE.md).
|
|
108
|
+
|
|
81
109
|
---
|
|
82
110
|
|
|
83
111
|
## One-Off Tasks Without a Roadmap
|
|
@@ -158,6 +186,8 @@ For Git, stop when the source checkout has modified or untracked files instead
|
|
|
158
186
|
of silently excluding them. Retain every isolated working copy until its changes
|
|
159
187
|
have been recovered. The packaged skill includes a minimal Git example.
|
|
160
188
|
|
|
189
|
+
For a roadmap loop, see [docs/WORKSPACE.md](docs/WORKSPACE.md).
|
|
190
|
+
|
|
161
191
|
### Teaching Your Agent to Use It
|
|
162
192
|
|
|
163
193
|
Lemming ships an Agent Skill so that an agent already running — Codex, Claude
|
|
@@ -246,6 +276,9 @@ conventions control their behavior:
|
|
|
246
276
|
runs before `90-roadmap.md`); files without a prefix default to priority 50.
|
|
247
277
|
- **Failure hooks**: Hooks at priority 90 and above also run when a task fails;
|
|
248
278
|
all others only run on success.
|
|
279
|
+
- **Oneshot tasks**: A task added with `--oneshot` skips all hooks once it
|
|
280
|
+
completes, so mechanical work (a typo fix, a version bump) does not pay for
|
|
281
|
+
the full review lifecycle. Failure hooks still run if it fails.
|
|
249
282
|
- **Disabling**: An empty file masks (disables) the hook of the same name from a
|
|
250
283
|
lower-precedence layer.
|
|
251
284
|
|
|
@@ -349,17 +382,24 @@ These come before the subcommand and apply to all of them.
|
|
|
349
382
|
- `--brief`: Omit task descriptions, which otherwise dominate the output.
|
|
350
383
|
- **`goal [<text>]`**: Set or view the long-term goal shared by all tasks.
|
|
351
384
|
Supports `-f/--file`.
|
|
352
|
-
- **`add <desc>`**: Append a new task. Supports `--index`, `--runner`,
|
|
353
|
-
`--model`.
|
|
354
|
-
- **`edit <id>`**: Modify a task's description, runner, model, or
|
|
385
|
+
- **`add <desc>`**: Append a new task. Supports `--index`, `--runner`,
|
|
386
|
+
`--model`, and `--oneshot`.
|
|
387
|
+
- **`edit <id>`**: Modify a task's description, runner, model, position, or
|
|
388
|
+
oneshot flag (`--oneshot/--no-oneshot`).
|
|
355
389
|
- **`brief <id> [text]`**: View or set a task's long-form brief. Unlike the
|
|
356
390
|
description it has no length cap, and it is appended to the runner prompt
|
|
357
391
|
automatically — the right home for measured timings, exact failing selectors,
|
|
358
392
|
or why a previous attempt was wrong. Supports `-f/--file`.
|
|
359
|
-
- **`
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
393
|
+
- **`artifact <id> [name] [text]`**: Store or read a task's verbose
|
|
394
|
+
diagnostics — stack traces, test dumps, long logs — outside `tasks.yml`, so
|
|
395
|
+
they never bloat the prompts of later attempts. With no name it lists what the
|
|
396
|
+
task has stored; with `--note` it also records the one-line progress pointer
|
|
397
|
+
for you. Later attempts see the artifact names in their prompt automatically.
|
|
398
|
+
Supports `-f/--file`.
|
|
399
|
+
- **`delete <id>`**: Remove an unstarted task while retaining its runner log;
|
|
400
|
+
its artifacts go with it. Tasks with execution history require `--force`;
|
|
401
|
+
autonomous restructuring should use `supersede`. Supports `--all` and
|
|
402
|
+
`--completed` for bulk cleanup, including logs.
|
|
363
403
|
- **`supersede <id> --reason <text>`**: Retire a replaced or split task without
|
|
364
404
|
losing its progress, timings, log, or links to replacement tasks.
|
|
365
405
|
- **`progress`**: Manage progress entries and findings for specific tasks.
|
|
@@ -468,6 +508,10 @@ lemming -C ~/src/other-project add "check --fix drops the trailing newline"
|
|
|
468
508
|
# Attach the evidence; the brief has no length cap
|
|
469
509
|
lemming -C ~/src/other-project brief <id> --file repro.md
|
|
470
510
|
|
|
511
|
+
# Park a failed attempt's output out of band, with a one-line pointer
|
|
512
|
+
lemming -C ~/src/other-project artifact <id> pytest.log --file pytest.log \
|
|
513
|
+
--note 'suite fails in teardown'
|
|
514
|
+
|
|
471
515
|
# Read the other project's roadmap without leaving yours
|
|
472
516
|
lemming -C ~/src/other-project status
|
|
473
517
|
```
|
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: lemming-cli
|
|
3
|
-
Version: 0.5.2
|
|
4
|
-
Summary: An autonomous, iterative task runner for AI coding agents.
|
|
5
|
-
Project-URL: Homepage, https://github.com/owahltinez/lemming
|
|
6
|
-
Project-URL: Repository, https://github.com/owahltinez/lemming
|
|
7
|
-
Author: Lemming Contributors
|
|
8
|
-
License: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Keywords: agents,ai,antigravity,automation,claude,opencode
|
|
11
|
-
Requires-Python: >=3.12
|
|
12
|
-
Requires-Dist: beautifulsoup4>=4.13.3
|
|
13
|
-
Requires-Dist: click-agentcli>=0.4.1
|
|
14
|
-
Requires-Dist: click>=8.0.0
|
|
15
|
-
Requires-Dist: fastapi>=0.124.4
|
|
16
|
-
Requires-Dist: markdownify>=0.14.1
|
|
17
|
-
Requires-Dist: pydantic>=2.12.5
|
|
18
|
-
Requires-Dist: pyyaml>=6.0.3
|
|
19
|
-
Requires-Dist: readability-cli>=0.10.0
|
|
20
|
-
Requires-Dist: requests>=2.32.3
|
|
21
|
-
Requires-Dist: uvicorn>=0.33.0
|
|
22
|
-
Description-Content-Type: text/markdown
|
|
23
|
-
|
|
24
1
|
# Lemming 🐹
|
|
25
2
|
|
|
26
3
|
**The transparent, tool-agnostic orchestrator for autonomous AI coding agents.**
|
|
@@ -101,6 +78,11 @@ lemming run
|
|
|
101
78
|
lemming run -- --model claude-3-5-sonnet
|
|
102
79
|
```
|
|
103
80
|
|
|
81
|
+
The loop runs every task in one shared workspace, and the orchestrator never
|
|
82
|
+
branches or commits on your behalf. Give the whole run its own branch or
|
|
83
|
+
worktree first if you want to be able to throw it away — see
|
|
84
|
+
[docs/WORKSPACE.md](docs/WORKSPACE.md).
|
|
85
|
+
|
|
104
86
|
---
|
|
105
87
|
|
|
106
88
|
## One-Off Tasks Without a Roadmap
|
|
@@ -181,6 +163,8 @@ For Git, stop when the source checkout has modified or untracked files instead
|
|
|
181
163
|
of silently excluding them. Retain every isolated working copy until its changes
|
|
182
164
|
have been recovered. The packaged skill includes a minimal Git example.
|
|
183
165
|
|
|
166
|
+
For a roadmap loop, see [docs/WORKSPACE.md](docs/WORKSPACE.md).
|
|
167
|
+
|
|
184
168
|
### Teaching Your Agent to Use It
|
|
185
169
|
|
|
186
170
|
Lemming ships an Agent Skill so that an agent already running — Codex, Claude
|
|
@@ -269,6 +253,9 @@ conventions control their behavior:
|
|
|
269
253
|
runs before `90-roadmap.md`); files without a prefix default to priority 50.
|
|
270
254
|
- **Failure hooks**: Hooks at priority 90 and above also run when a task fails;
|
|
271
255
|
all others only run on success.
|
|
256
|
+
- **Oneshot tasks**: A task added with `--oneshot` skips all hooks once it
|
|
257
|
+
completes, so mechanical work (a typo fix, a version bump) does not pay for
|
|
258
|
+
the full review lifecycle. Failure hooks still run if it fails.
|
|
272
259
|
- **Disabling**: An empty file masks (disables) the hook of the same name from a
|
|
273
260
|
lower-precedence layer.
|
|
274
261
|
|
|
@@ -372,17 +359,24 @@ These come before the subcommand and apply to all of them.
|
|
|
372
359
|
- `--brief`: Omit task descriptions, which otherwise dominate the output.
|
|
373
360
|
- **`goal [<text>]`**: Set or view the long-term goal shared by all tasks.
|
|
374
361
|
Supports `-f/--file`.
|
|
375
|
-
- **`add <desc>`**: Append a new task. Supports `--index`, `--runner`,
|
|
376
|
-
`--model`.
|
|
377
|
-
- **`edit <id>`**: Modify a task's description, runner, model, or
|
|
362
|
+
- **`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`).
|
|
378
366
|
- **`brief <id> [text]`**: View or set a task's long-form brief. Unlike the
|
|
379
367
|
description it has no length cap, and it is appended to the runner prompt
|
|
380
368
|
automatically — the right home for measured timings, exact failing selectors,
|
|
381
369
|
or why a previous attempt was wrong. Supports `-f/--file`.
|
|
382
|
-
- **`
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
370
|
+
- **`artifact <id> [name] [text]`**: Store or read a task's verbose
|
|
371
|
+
diagnostics — stack traces, test dumps, long logs — outside `tasks.yml`, so
|
|
372
|
+
they never bloat the prompts of later attempts. With no name it lists what the
|
|
373
|
+
task has stored; with `--note` it also records the one-line progress pointer
|
|
374
|
+
for you. Later attempts see the artifact names in their prompt automatically.
|
|
375
|
+
Supports `-f/--file`.
|
|
376
|
+
- **`delete <id>`**: Remove an unstarted task while retaining its runner log;
|
|
377
|
+
its artifacts go with it. Tasks with execution history require `--force`;
|
|
378
|
+
autonomous restructuring should use `supersede`. Supports `--all` and
|
|
379
|
+
`--completed` for bulk cleanup, including logs.
|
|
386
380
|
- **`supersede <id> --reason <text>`**: Retire a replaced or split task without
|
|
387
381
|
losing its progress, timings, log, or links to replacement tasks.
|
|
388
382
|
- **`progress`**: Manage progress entries and findings for specific tasks.
|
|
@@ -491,6 +485,10 @@ lemming -C ~/src/other-project add "check --fix drops the trailing newline"
|
|
|
491
485
|
# Attach the evidence; the brief has no length cap
|
|
492
486
|
lemming -C ~/src/other-project brief <id> --file repro.md
|
|
493
487
|
|
|
488
|
+
# Park a failed attempt's output out of band, with a one-line pointer
|
|
489
|
+
lemming -C ~/src/other-project artifact <id> pytest.log --file pytest.log \
|
|
490
|
+
--note 'suite fails in teardown'
|
|
491
|
+
|
|
494
492
|
# Read the other project's roadmap without leaving yours
|
|
495
493
|
lemming -C ~/src/other-project status
|
|
496
494
|
```
|
|
@@ -156,6 +156,10 @@ lemming add "Queue writes in IndexedDB while offline"
|
|
|
156
156
|
lemming run # runs until the queue drains
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
+
The loop shares one workspace across all its tasks, so isolate the **whole run**
|
|
160
|
+
— one branch or worktree created before `lemming run`, driven with
|
|
161
|
+
`lemming -C <worktree> run` — never one per task.
|
|
162
|
+
|
|
159
163
|
Check on it, and read what an agent actually did:
|
|
160
164
|
|
|
161
165
|
```sh
|
|
@@ -173,7 +177,9 @@ lemming stop --after-current-task # drain, then stop
|
|
|
173
177
|
|
|
174
178
|
After each task, review hooks run automatically and a roadmap hook may revise
|
|
175
179
|
the queue. `lemming hooks list` shows them; `lemming hooks disable <name>` turns
|
|
176
|
-
one off for the project.
|
|
180
|
+
one off for the project. Queue a mechanical task — a typo fix, a version bump, a
|
|
181
|
+
rename — with `lemming add "..." --oneshot` to skip those hooks when it
|
|
182
|
+
completes.
|
|
177
183
|
|
|
178
184
|
## What to know before running any of it
|
|
179
185
|
|
|
@@ -31,6 +31,11 @@ conventions control their behavior:
|
|
|
31
31
|
- **Failure hooks**: Hooks at priority 90 and above also run when a task fails.
|
|
32
32
|
All other hooks only run after successful tasks. The built-in `roadmap` hook
|
|
33
33
|
ships as `90-roadmap.md` so it can react to failures.
|
|
34
|
+
- **Oneshot tasks**: No hook runs after a task marked `--oneshot` completes,
|
|
35
|
+
keeping mechanical changes cheap. A oneshot task that fails still gets its
|
|
36
|
+
failure hooks, so recovery is never skipped. Note that `roadmap` is skipped
|
|
37
|
+
too: if a oneshot task is the last one in the queue, the run ends without a
|
|
38
|
+
goal audit, so nothing is queued to carry the goal further.
|
|
34
39
|
- **Masking**: An empty file disables the hook of the same name from a
|
|
35
40
|
lower-precedence layer (see below). For example, an empty
|
|
36
41
|
`.lemming/hooks/readability.md` disables the built-in `readability` hook for
|
|
@@ -72,6 +77,33 @@ gaps, and exits immediately when the task did not change user-facing behavior.
|
|
|
72
77
|
Its prompt instructs the agent to stay advisory, but this is not
|
|
73
78
|
sandbox-enforced. It runs after `testing` and before `roadmap`.
|
|
74
79
|
|
|
80
|
+
### Rejecting Completion
|
|
81
|
+
|
|
82
|
+
A hook that finds a real defect it cannot fix should not exit 0 and let the
|
|
83
|
+
task be marked completed. It rejects the completion instead:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
lemming --tasks-file <path> reject <taskid> '<reason>'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A rejection only applies to a task that is finalizing as completed, and it
|
|
90
|
+
does not change the task's status on the spot. The remaining hooks still run
|
|
91
|
+
and see the task as `REJECTED` with the reason, so the roadmap hook can tell
|
|
92
|
+
a rejected task from a finished one. Once every hook has run, the task is
|
|
93
|
+
reverted to pending with its attempt count intact, exactly as it is when a
|
|
94
|
+
hook process fails, and the reason is left in its progress so the next
|
|
95
|
+
attempt starts with the diagnosis.
|
|
96
|
+
|
|
97
|
+
Under `lemming exec` there is no second attempt: the run ends on the first
|
|
98
|
+
rejection, and under `lemming run` a rejection on the task's final attempt
|
|
99
|
+
sends it straight to the exhausted-retries path, where the failure hooks get
|
|
100
|
+
their chance to repair it.
|
|
101
|
+
|
|
102
|
+
Reject only for an objective, reproducible failure the hook could not repair
|
|
103
|
+
itself, such as a failing test suite or a `lemming readability check` that
|
|
104
|
+
still fails. Advisory findings, and anything the hook already fixed, belong
|
|
105
|
+
in `lemming progress`.
|
|
106
|
+
|
|
75
107
|
### Reviews Are Not Read-Only
|
|
76
108
|
|
|
77
109
|
When selected with `lemming exec --review`, the hooks above may edit the working
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Workspace Isolation 🧰
|
|
2
|
+
|
|
3
|
+
## Lemming is VCS-Agnostic
|
|
4
|
+
|
|
5
|
+
An agent run writes to a real directory on your disk. The orchestrator never
|
|
6
|
+
creates a branch, a commit, or a worktree, and never reverts anything: it runs
|
|
7
|
+
the agent in a directory and gets out of the way, so whatever a run leaves
|
|
8
|
+
behind — finished work, half-applied edits, scratch files — is what you get. The
|
|
9
|
+
task-runner prompt does allow the agent itself to commit when that is the
|
|
10
|
+
project's convention, but the orchestrator neither asks for that nor relies on
|
|
11
|
+
it.
|
|
12
|
+
|
|
13
|
+
This is deliberate: branching would tie the orchestrator to Git, and Lemming is
|
|
14
|
+
expected to work in a Mercurial repository, a Perforce client, or a plain
|
|
15
|
+
directory under no version control at all. No portable "undo the last run"
|
|
16
|
+
primitive exists across those, so **isolation and recovery are the caller's
|
|
17
|
+
job**. If you need to be able to throw a run away, set that up before it starts.
|
|
18
|
+
|
|
19
|
+
## Isolating a One-Off `exec` Run
|
|
20
|
+
|
|
21
|
+
Concurrent writing runs must not share a checkout: two agents editing one
|
|
22
|
+
directory interleave their edits, and neither result is reviewable. Give each
|
|
23
|
+
writing run its own working copy and pass it with the global `-C` option
|
|
24
|
+
(`lemming -C <working-copy> -v exec ...`). The full Git worktree recipe is under
|
|
25
|
+
"Isolate concurrent writing runs" in [SKILL.md](../SKILL.md), summarized in the
|
|
26
|
+
[README](../README.md#one-off-tasks-without-a-roadmap).
|
|
27
|
+
|
|
28
|
+
## Isolating a Roadmap Loop
|
|
29
|
+
|
|
30
|
+
`lemming run` executes its tasks **sequentially in one shared workspace**. Task
|
|
31
|
+
2 starts in whatever state task 1 left behind — that is the point, since tasks
|
|
32
|
+
build on each other. So the unit of isolation is the whole run: **one branch or
|
|
33
|
+
worktree for the entire loop**, created once before `lemming run`.
|
|
34
|
+
|
|
35
|
+
**Never one branch per task.** Whichever branch the last runner left checked out
|
|
36
|
+
would silently become the next task's baseline, and the loop's history would
|
|
37
|
+
fragment across branches nobody asked for. This is why the task-runner prompt
|
|
38
|
+
says nothing about branching: a runner has no business changing what is checked
|
|
39
|
+
out under it.
|
|
40
|
+
|
|
41
|
+
The minimal Git shape, from a clean tree:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
repo=$(git rev-parse --show-toplevel) || exit 1
|
|
45
|
+
test -z "$(git -C "$repo" status --porcelain)" || exit 1
|
|
46
|
+
|
|
47
|
+
# One worktree for the whole run, from committed HEAD.
|
|
48
|
+
worktree=$(mktemp -d "${TMPDIR:-/tmp}/lemming-run.XXXXXX") || exit 1
|
|
49
|
+
branch="lemming/$(basename "$worktree")"
|
|
50
|
+
git -C "$repo" worktree add -b "$branch" "$worktree" HEAD || exit 1
|
|
51
|
+
|
|
52
|
+
# Drive the loop against it; -C is a global option, before the subcommand.
|
|
53
|
+
lemming -C "$worktree" goal "<the long-term goal>"
|
|
54
|
+
lemming -C "$worktree" add "<first task>"
|
|
55
|
+
lemming -C "$worktree" -v run
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A plain branch (`git switch -c lemming/<run>`) does just as well when nothing
|
|
59
|
+
else needs the checkout; the worktree only buys you the original directory to
|
|
60
|
+
keep working in meanwhile. Either way the roadmap travels with the workspace: a
|
|
61
|
+
tracked `tasks.yml` is copied into the worktree and the loop's edits stay there,
|
|
62
|
+
and without one Lemming keeps isolated state keyed on the worktree path.
|
|
63
|
+
|
|
64
|
+
## Recovering After an Aborted Run
|
|
65
|
+
|
|
66
|
+
With a pre-run baseline, recovery is ordinary version control, and `-C` reaches
|
|
67
|
+
the run's own records without your having to know where they are stored:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git -C "$worktree" status # what the run touched
|
|
71
|
+
git -C "$worktree" log HEAD # commits, if the runners made any
|
|
72
|
+
git -C "$worktree" diff # whatever is still uncommitted
|
|
73
|
+
|
|
74
|
+
lemming -C "$worktree" status # queue, attempts, recorded progress
|
|
75
|
+
lemming -C "$worktree" logs <id> # the full runner log for one task
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Keep the branch, or discard it wholesale. There is no partial rollback of one
|
|
79
|
+
task inside a loop — the tasks share a workspace, so their edits are not
|
|
80
|
+
separable unless the runners committed them separately.
|
|
81
|
+
|
|
82
|
+
Runners are also told to write verbose evidence into Lemming's per-project
|
|
83
|
+
scratch directory rather than into the workspace, so a failed task's notes
|
|
84
|
+
survive even after you throw the branch away. It sits under `~/.local/lemming/`
|
|
85
|
+
(or `$LEMMING_HOME`), named for the first 12 hex digits of the SHA-256 of the
|
|
86
|
+
resolved tasks-file path — or of the workspace path, when Lemming is keeping the
|
|
87
|
+
roadmap itself. Once the worktree is gone `-C` can no longer resolve it, and
|
|
88
|
+
`lemming --tasks-file <that dir>/tasks.yml logs <id>` is the way back in.
|
|
89
|
+
|
|
90
|
+
So retain the worktree until its changes have been recovered, then clean up
|
|
91
|
+
non-forcibly, which refuses rather than destroying uncommitted work — including
|
|
92
|
+
a tracked `tasks.yml` the loop has edited, which must be committed or discarded
|
|
93
|
+
first:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
git -C "$repo" worktree remove "$worktree"
|
|
97
|
+
git -C "$repo" branch -d "$branch"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## What the Runner Prompt Guarantees
|
|
101
|
+
|
|
102
|
+
Runners are instructed to leave a coherent workspace, to back out their own
|
|
103
|
+
half-applied edits before failing, and never to restore files from version
|
|
104
|
+
control history; the rules live in the "Workspace Hygiene" directive of
|
|
105
|
+
`src/lemming/prompts/taskrunner.md`, not in a copy here. Treat them as a strong
|
|
106
|
+
prior, not a guarantee: runners are LLMs, a prompt is guidance rather than
|
|
107
|
+
enforcement, and a run killed by a timeout never gets to clean up at all. The
|
|
108
|
+
branch or worktree is what actually makes a bad run recoverable; the prompt only
|
|
109
|
+
reduces how often you need it.
|
|
110
|
+
|
|
111
|
+
## Workspaces Not Under Version Control
|
|
112
|
+
|
|
113
|
+
If the workspace is a plain directory, there is nothing to diff against and
|
|
114
|
+
nothing to reset. **Copy the directory before the run**; that copy is the only
|
|
115
|
+
rollback available.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
cp -a project project.bak
|
|
119
|
+
lemming -C project run
|
|
120
|
+
```
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lemming-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.0"
|
|
8
8
|
description = "An autonomous, iterative task runner for AI coding agents."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -16,7 +16,7 @@ keywords = ["ai", "agents", "automation", "antigravity", "claude", "opencode"]
|
|
|
16
16
|
dependencies = [
|
|
17
17
|
"beautifulsoup4>=4.13.3",
|
|
18
18
|
"click>=8.0.0",
|
|
19
|
-
"click-agentcli>=0.
|
|
19
|
+
"click-agentcli>=0.5.0",
|
|
20
20
|
"fastapi>=0.124.4",
|
|
21
21
|
"markdownify>=0.14.1",
|
|
22
22
|
"pydantic>=2.12.5",
|
|
@@ -49,7 +49,7 @@ Homepage = "https://github.com/owahltinez/lemming"
|
|
|
49
49
|
Repository = "https://github.com/owahltinez/lemming"
|
|
50
50
|
|
|
51
51
|
[project.scripts]
|
|
52
|
-
lemming = "lemming.main:
|
|
52
|
+
lemming = "lemming.main:main"
|
|
53
53
|
|
|
54
54
|
[tool.hatch.metadata]
|
|
55
55
|
allow-direct-references = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""HTTP API for the lemming web UI, built on FastAPI."""
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import fastapi.testclient
|
|
2
2
|
|
|
3
|
-
from lemming import
|
|
3
|
+
from lemming.api import main
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def test_share_token_middleware():
|
|
7
7
|
# Setup test client
|
|
8
|
-
original_token = getattr(
|
|
8
|
+
original_token = getattr(main.app.state, "share_token", None)
|
|
9
9
|
try:
|
|
10
|
-
|
|
10
|
+
main.app.state.share_token = "secret123"
|
|
11
11
|
# We need a fresh client for each test that modifies app state
|
|
12
12
|
# middleware if it uses the app state, but here TestClient is
|
|
13
|
-
# created with
|
|
14
|
-
client = fastapi.testclient.TestClient(
|
|
13
|
+
# created with main.app
|
|
14
|
+
client = fastapi.testclient.TestClient(main.app)
|
|
15
15
|
|
|
16
16
|
# Missing token -> 401
|
|
17
17
|
response = client.get("/api/data")
|
|
@@ -36,15 +36,15 @@ def test_share_token_middleware():
|
|
|
36
36
|
assert response.status_code == 401, host
|
|
37
37
|
finally:
|
|
38
38
|
# Restore
|
|
39
|
-
|
|
39
|
+
main.app.state.share_token = original_token
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def test_share_token_middleware_rejects_exotic_tokens():
|
|
43
43
|
"""Odd tokens must be rejected, not crash the comparison."""
|
|
44
|
-
original_token = getattr(
|
|
44
|
+
original_token = getattr(main.app.state, "share_token", None)
|
|
45
45
|
try:
|
|
46
|
-
|
|
47
|
-
client = fastapi.testclient.TestClient(
|
|
46
|
+
main.app.state.share_token = "secret123"
|
|
47
|
+
client = fastapi.testclient.TestClient(main.app)
|
|
48
48
|
|
|
49
49
|
# Non-ASCII and differing lengths must stay a clean 401. A digest
|
|
50
50
|
# comparison rejects both only if the values are encoded first.
|
|
@@ -53,15 +53,15 @@ def test_share_token_middleware_rejects_exotic_tokens():
|
|
|
53
53
|
assert response.status_code == 401, token
|
|
54
54
|
finally:
|
|
55
55
|
# Restore
|
|
56
|
-
|
|
56
|
+
main.app.state.share_token = original_token
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
def test_share_token_middleware_inert_without_token():
|
|
60
60
|
"""Local (non-tunnel) mode sets no share token, so nothing is required."""
|
|
61
|
-
original_token = getattr(
|
|
61
|
+
original_token = getattr(main.app.state, "share_token", None)
|
|
62
62
|
try:
|
|
63
|
-
|
|
64
|
-
client = fastapi.testclient.TestClient(
|
|
63
|
+
main.app.state.share_token = None
|
|
64
|
+
client = fastapi.testclient.TestClient(main.app)
|
|
65
65
|
|
|
66
66
|
response = client.get("/api/data")
|
|
67
67
|
assert response.status_code == 200
|
|
@@ -70,4 +70,4 @@ def test_share_token_middleware_inert_without_token():
|
|
|
70
70
|
)
|
|
71
71
|
finally:
|
|
72
72
|
# Restore
|
|
73
|
-
|
|
73
|
+
main.app.state.share_token = original_token
|
|
@@ -7,7 +7,8 @@ import sys
|
|
|
7
7
|
import fastapi
|
|
8
8
|
import pydantic
|
|
9
9
|
|
|
10
|
-
from .. import models,
|
|
10
|
+
from .. import models, persistence
|
|
11
|
+
from ..tasks import operations
|
|
11
12
|
from . import context
|
|
12
13
|
|
|
13
14
|
router = fastapi.APIRouter()
|
|
@@ -25,22 +26,22 @@ def update_goal(
|
|
|
25
26
|
):
|
|
26
27
|
"""Update the long-term goal shared with all task runners."""
|
|
27
28
|
tasks_file = context.resolve_tasks_file(request.app.state, project)
|
|
28
|
-
|
|
29
|
+
operations.update_goal(tasks_file, update.get("goal", ""))
|
|
29
30
|
return {"status": "ok"}
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
@router.post("/api/config")
|
|
33
34
|
def update_config(
|
|
34
35
|
request: fastapi.Request,
|
|
35
|
-
config:
|
|
36
|
+
config: models.RoadmapConfig,
|
|
36
37
|
project: str | None = None,
|
|
37
38
|
):
|
|
38
39
|
"""Replace the roadmap configuration and return the saved value."""
|
|
39
40
|
tasks_file = context.resolve_tasks_file(request.app.state, project)
|
|
40
|
-
with
|
|
41
|
-
data =
|
|
41
|
+
with persistence.lock_tasks(tasks_file):
|
|
42
|
+
data = persistence.load_tasks(tasks_file)
|
|
42
43
|
data.config = config
|
|
43
|
-
|
|
44
|
+
persistence.save_tasks(tasks_file, data)
|
|
44
45
|
return data.config
|
|
45
46
|
|
|
46
47
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from unittest.mock import patch
|
|
2
2
|
|
|
3
|
-
from lemming import
|
|
3
|
+
from lemming.api import main
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def test_run_loop(client, test_tasks):
|
|
@@ -25,7 +25,7 @@ def test_get_runners(client):
|
|
|
25
25
|
|
|
26
26
|
def test_project_goal_isolation(client, test_tasks):
|
|
27
27
|
"""The goal is isolated per project."""
|
|
28
|
-
root =
|
|
28
|
+
root = main.app.state.root
|
|
29
29
|
subdir = root / "goal_test"
|
|
30
30
|
subdir.mkdir(exist_ok=True)
|
|
31
31
|
|
|
@@ -50,7 +50,7 @@ def test_project_goal_isolation(client, test_tasks):
|
|
|
50
50
|
|
|
51
51
|
def test_run_loop_with_project(client, test_tasks):
|
|
52
52
|
"""POST /api/run with project param uses the correct tasks file."""
|
|
53
|
-
root =
|
|
53
|
+
root = main.app.state.root
|
|
54
54
|
subdir = root / "run_project"
|
|
55
55
|
subdir.mkdir(exist_ok=True)
|
|
56
56
|
(subdir / "tasks.yml").touch()
|