lemming-cli 0.1.0__py3-none-any.whl
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/__init__.py +1 -0
- lemming/api/__init__.py +5 -0
- lemming/api/auth.py +34 -0
- lemming/api/auth_test.py +40 -0
- lemming/api/config.py +85 -0
- lemming/api/config_test.py +84 -0
- lemming/api/conftest.py +204 -0
- lemming/api/context.py +39 -0
- lemming/api/context_test.py +62 -0
- lemming/api/directories.py +57 -0
- lemming/api/directories_test.py +94 -0
- lemming/api/files.py +116 -0
- lemming/api/files_test.py +163 -0
- lemming/api/hooks.py +15 -0
- lemming/api/hooks_test.py +33 -0
- lemming/api/logging.py +16 -0
- lemming/api/logging_test.py +59 -0
- lemming/api/loop.py +45 -0
- lemming/api/loop_test.py +58 -0
- lemming/api/main.py +53 -0
- lemming/api/main_test.py +30 -0
- lemming/api/tasks.py +170 -0
- lemming/api/tasks_test.py +426 -0
- lemming/api.py +5 -0
- lemming/api_test.py +7 -0
- lemming/cli/__init__.py +12 -0
- lemming/cli/config.py +67 -0
- lemming/cli/config_test.py +50 -0
- lemming/cli/context.py +40 -0
- lemming/cli/context_test.py +49 -0
- lemming/cli/hooks.py +147 -0
- lemming/cli/hooks_test.py +50 -0
- lemming/cli/main.py +42 -0
- lemming/cli/main_test.py +21 -0
- lemming/cli/operations.py +226 -0
- lemming/cli/operations_test.py +44 -0
- lemming/cli/progress.py +54 -0
- lemming/cli/progress_test.py +40 -0
- lemming/cli/readability_cli.py +28 -0
- lemming/cli/readability_cli_test.py +57 -0
- lemming/cli/tasks.py +529 -0
- lemming/cli/tasks_test.py +168 -0
- lemming/cli.py +5 -0
- lemming/cli_test.py +22 -0
- lemming/conftest.py +13 -0
- lemming/hooks.py +145 -0
- lemming/hooks_test.py +180 -0
- lemming/integration_test.py +299 -0
- lemming/main.py +6 -0
- lemming/main_test.py +44 -0
- lemming/models.py +88 -0
- lemming/models_test.py +91 -0
- lemming/orchestrator.py +407 -0
- lemming/orchestrator_test.py +468 -0
- lemming/paths.py +245 -0
- lemming/paths_test.py +186 -0
- lemming/persistence.py +179 -0
- lemming/persistence_test.py +150 -0
- lemming/prompts/hooks/readability.md +42 -0
- lemming/prompts/hooks/roadmap.md +61 -0
- lemming/prompts/hooks/testing.md +44 -0
- lemming/prompts/taskrunner.md +69 -0
- lemming/prompts.py +354 -0
- lemming/prompts_test.py +421 -0
- lemming/providers.py +190 -0
- lemming/providers_test.py +73 -0
- lemming/runner.py +327 -0
- lemming/runner_test.py +378 -0
- lemming/tasks/__init__.py +75 -0
- lemming/tasks/lifecycle.py +331 -0
- lemming/tasks/lifecycle_test.py +312 -0
- lemming/tasks/operations.py +258 -0
- lemming/tasks/operations_test.py +172 -0
- lemming/tasks/progress.py +29 -0
- lemming/tasks/progress_test.py +22 -0
- lemming/tasks/queries.py +128 -0
- lemming/tasks/queries_test.py +233 -0
- lemming/web/dashboard.spec.js +350 -0
- lemming/web/dashboard.test.js +998 -0
- lemming/web/favicon.js +40 -0
- lemming/web/favicon.spec.js +242 -0
- lemming/web/files.html +375 -0
- lemming/web/files.spec.js +97 -0
- lemming/web/index.html +983 -0
- lemming/web/index.js +753 -0
- lemming/web/logs.html +358 -0
- lemming/web/logs.test.js +195 -0
- lemming/web/mancha.js +58 -0
- lemming/web/screenshots.spec.js +328 -0
- lemming_cli-0.1.0.dist-info/METADATA +314 -0
- lemming_cli-0.1.0.dist-info/RECORD +94 -0
- lemming_cli-0.1.0.dist-info/WHEEL +4 -0
- lemming_cli-0.1.0.dist-info/entry_points.txt +2 -0
- lemming_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
import shutil
|
|
3
|
+
import tempfile
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
import click.testing
|
|
7
|
+
|
|
8
|
+
from lemming import cli, tasks
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestCLIOperations(unittest.TestCase):
|
|
12
|
+
def setUp(self):
|
|
13
|
+
self.cli_runner = click.testing.CliRunner()
|
|
14
|
+
self.test_dir = tempfile.mkdtemp()
|
|
15
|
+
self.test_tasks_file = pathlib.Path(self.test_dir) / "tasks_test.yml"
|
|
16
|
+
self.base_args = [
|
|
17
|
+
"--verbose",
|
|
18
|
+
"--tasks-file",
|
|
19
|
+
str(self.test_tasks_file),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# Scaffold a valid file
|
|
23
|
+
data = tasks.Roadmap(
|
|
24
|
+
context="Initial context",
|
|
25
|
+
tasks=[],
|
|
26
|
+
)
|
|
27
|
+
tasks.save_tasks(self.test_tasks_file, data)
|
|
28
|
+
|
|
29
|
+
def tearDown(self):
|
|
30
|
+
shutil.rmtree(self.test_dir)
|
|
31
|
+
|
|
32
|
+
def test_run_help(self):
|
|
33
|
+
result = self.cli_runner.invoke(cli.cli, ["run", "--help"])
|
|
34
|
+
self.assertEqual(result.exit_code, 0)
|
|
35
|
+
self.assertIn("Starts the orchestrator loop", result.output)
|
|
36
|
+
|
|
37
|
+
def test_serve_help(self):
|
|
38
|
+
result = self.cli_runner.invoke(cli.cli, ["serve", "--help"])
|
|
39
|
+
self.assertEqual(result.exit_code, 0)
|
|
40
|
+
self.assertIn("Launches the local web dashboard", result.output)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
unittest.main()
|
lemming/cli/progress.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""CLI command for recording task progress entries."""
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from .. import tasks
|
|
8
|
+
from .main import cli
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@cli.command(short_help="<taskid> [text] Add a progress entry")
|
|
12
|
+
@click.argument("task_id")
|
|
13
|
+
@click.argument("text", required=False)
|
|
14
|
+
@click.option(
|
|
15
|
+
"--file",
|
|
16
|
+
"-f",
|
|
17
|
+
type=click.File("r"),
|
|
18
|
+
help="Read progress text from a file (or - for stdin).",
|
|
19
|
+
)
|
|
20
|
+
@click.pass_context
|
|
21
|
+
def progress(
|
|
22
|
+
ctx: click.Context,
|
|
23
|
+
task_id: str,
|
|
24
|
+
text: typing.Optional[str],
|
|
25
|
+
file: typing.Optional[typing.TextIO],
|
|
26
|
+
):
|
|
27
|
+
"""Records a progress entry or finding for a specific task.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
ctx: The click context holding shared CLI state.
|
|
31
|
+
task_id: The ID of the task.
|
|
32
|
+
text: The progress to record (optional if --file is used).
|
|
33
|
+
file: An optional file to read the progress from.
|
|
34
|
+
"""
|
|
35
|
+
tasks_file = ctx.obj["TASKS_FILE"]
|
|
36
|
+
|
|
37
|
+
if file:
|
|
38
|
+
if text:
|
|
39
|
+
click.echo("Error: Cannot provide both progress text and --file.")
|
|
40
|
+
ctx.exit(1)
|
|
41
|
+
text = file.read().strip()
|
|
42
|
+
elif text:
|
|
43
|
+
text = text.strip()
|
|
44
|
+
|
|
45
|
+
if not text:
|
|
46
|
+
click.echo("Error: Must provide either progress text or --file.")
|
|
47
|
+
ctx.exit(1)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
target_task = tasks.add_progress(tasks_file, task_id, text)
|
|
51
|
+
click.echo(f"Progress added to task {target_task.id}.")
|
|
52
|
+
except ValueError as e:
|
|
53
|
+
click.echo(f"Error: {e}")
|
|
54
|
+
ctx.exit(1)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from click.testing import CliRunner
|
|
3
|
+
|
|
4
|
+
from lemming import tasks
|
|
5
|
+
from lemming.cli import main as cli
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def setup_env(tmp_path):
|
|
10
|
+
tasks_file = tmp_path / "tasks_test.yml"
|
|
11
|
+
base_args = ["--tasks-file", str(tasks_file)]
|
|
12
|
+
runner = CliRunner()
|
|
13
|
+
|
|
14
|
+
data = tasks.Roadmap(
|
|
15
|
+
context="Initial context",
|
|
16
|
+
tasks=[
|
|
17
|
+
tasks.Task(
|
|
18
|
+
id="12345678",
|
|
19
|
+
description="Test task",
|
|
20
|
+
status=tasks.TaskStatus.PENDING,
|
|
21
|
+
)
|
|
22
|
+
],
|
|
23
|
+
)
|
|
24
|
+
tasks.save_tasks(tasks_file, data)
|
|
25
|
+
|
|
26
|
+
return runner, base_args, tasks_file
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_progress(setup_env):
|
|
30
|
+
runner, base_args, tasks_file = setup_env
|
|
31
|
+
|
|
32
|
+
result = runner.invoke(
|
|
33
|
+
cli.cli, base_args + ["progress", "12345678", "Observed behavior X"]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
assert result.exit_code == 0
|
|
37
|
+
assert "Progress added to task" in result.output
|
|
38
|
+
|
|
39
|
+
data = tasks.load_tasks(tasks_file)
|
|
40
|
+
assert "Observed behavior X" in data.tasks[0].progress
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""CLI group wrapping the standalone readability code-quality tool."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
import readability
|
|
7
|
+
|
|
8
|
+
from .main import cli
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@cli.group(name="readability")
|
|
12
|
+
@click.pass_context
|
|
13
|
+
def readability_group(ctx: click.Context):
|
|
14
|
+
"""Run the readability tool for code quality checks.
|
|
15
|
+
|
|
16
|
+
This wraps the standalone 'readability' package, ensuring it is always
|
|
17
|
+
available to coding agents running within Lemming.
|
|
18
|
+
"""
|
|
19
|
+
# Sync verbose flag from lemming's top-level option
|
|
20
|
+
if ctx.obj.get("VERBOSE"):
|
|
21
|
+
logging.getLogger("readability").setLevel(logging.DEBUG)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Merge the commands from the readability package directly into our group.
|
|
25
|
+
# This allows 'lemming readability check ...' instead of
|
|
26
|
+
# 'lemming readability cli check ...'
|
|
27
|
+
for name, command in readability.cli.commands.items():
|
|
28
|
+
readability_group.add_command(command, name=name)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from click.testing import CliRunner
|
|
4
|
+
|
|
5
|
+
from lemming.cli.main import cli
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_readability_group_help():
|
|
9
|
+
runner = CliRunner()
|
|
10
|
+
result = runner.invoke(cli, ["readability", "--help"])
|
|
11
|
+
assert result.exit_code == 0
|
|
12
|
+
assert "Run the readability tool for code quality checks" in result.output
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_readability_check_help():
|
|
16
|
+
runner = CliRunner()
|
|
17
|
+
result = runner.invoke(cli, ["readability", "check", "--help"])
|
|
18
|
+
assert result.exit_code == 0
|
|
19
|
+
assert "check" in result.output.lower()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_readability_guide_help():
|
|
23
|
+
runner = CliRunner()
|
|
24
|
+
result = runner.invoke(cli, ["readability", "guide", "--help"])
|
|
25
|
+
assert result.exit_code == 0
|
|
26
|
+
assert "guide" in result.output.lower()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_readability_languages():
|
|
30
|
+
runner = CliRunner()
|
|
31
|
+
result = runner.invoke(cli, ["readability", "languages"])
|
|
32
|
+
assert result.exit_code == 0
|
|
33
|
+
assert "Supported languages" in result.output
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_readability_verbose_sync():
|
|
37
|
+
runner = CliRunner()
|
|
38
|
+
# This just ensures the command runs with -v,
|
|
39
|
+
# we can't easily check the logger level of a sub-process or if it was
|
|
40
|
+
# modified in-process
|
|
41
|
+
# without more complex mocking, but we can verify it doesn't crash.
|
|
42
|
+
result = runner.invoke(cli, ["-v", "readability", "languages"])
|
|
43
|
+
assert result.exit_code == 0
|
|
44
|
+
assert "Supported languages" in result.output
|
|
45
|
+
|
|
46
|
+
# Check that the logger level was actually set in this process
|
|
47
|
+
logger = logging.getLogger("readability")
|
|
48
|
+
assert logger.level == logging.DEBUG
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_readability_check_ignored_file():
|
|
52
|
+
runner = CliRunner()
|
|
53
|
+
# src/lemming/web/mancha.js is ignored in biome.json
|
|
54
|
+
result = runner.invoke(
|
|
55
|
+
cli, ["readability", "check", "src/lemming/web/mancha.js"]
|
|
56
|
+
)
|
|
57
|
+
assert result.exit_code == 0
|