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.
Files changed (94) hide show
  1. lemming/__init__.py +1 -0
  2. lemming/api/__init__.py +5 -0
  3. lemming/api/auth.py +34 -0
  4. lemming/api/auth_test.py +40 -0
  5. lemming/api/config.py +85 -0
  6. lemming/api/config_test.py +84 -0
  7. lemming/api/conftest.py +204 -0
  8. lemming/api/context.py +39 -0
  9. lemming/api/context_test.py +62 -0
  10. lemming/api/directories.py +57 -0
  11. lemming/api/directories_test.py +94 -0
  12. lemming/api/files.py +116 -0
  13. lemming/api/files_test.py +163 -0
  14. lemming/api/hooks.py +15 -0
  15. lemming/api/hooks_test.py +33 -0
  16. lemming/api/logging.py +16 -0
  17. lemming/api/logging_test.py +59 -0
  18. lemming/api/loop.py +45 -0
  19. lemming/api/loop_test.py +58 -0
  20. lemming/api/main.py +53 -0
  21. lemming/api/main_test.py +30 -0
  22. lemming/api/tasks.py +170 -0
  23. lemming/api/tasks_test.py +426 -0
  24. lemming/api.py +5 -0
  25. lemming/api_test.py +7 -0
  26. lemming/cli/__init__.py +12 -0
  27. lemming/cli/config.py +67 -0
  28. lemming/cli/config_test.py +50 -0
  29. lemming/cli/context.py +40 -0
  30. lemming/cli/context_test.py +49 -0
  31. lemming/cli/hooks.py +147 -0
  32. lemming/cli/hooks_test.py +50 -0
  33. lemming/cli/main.py +42 -0
  34. lemming/cli/main_test.py +21 -0
  35. lemming/cli/operations.py +226 -0
  36. lemming/cli/operations_test.py +44 -0
  37. lemming/cli/progress.py +54 -0
  38. lemming/cli/progress_test.py +40 -0
  39. lemming/cli/readability_cli.py +28 -0
  40. lemming/cli/readability_cli_test.py +57 -0
  41. lemming/cli/tasks.py +529 -0
  42. lemming/cli/tasks_test.py +168 -0
  43. lemming/cli.py +5 -0
  44. lemming/cli_test.py +22 -0
  45. lemming/conftest.py +13 -0
  46. lemming/hooks.py +145 -0
  47. lemming/hooks_test.py +180 -0
  48. lemming/integration_test.py +299 -0
  49. lemming/main.py +6 -0
  50. lemming/main_test.py +44 -0
  51. lemming/models.py +88 -0
  52. lemming/models_test.py +91 -0
  53. lemming/orchestrator.py +407 -0
  54. lemming/orchestrator_test.py +468 -0
  55. lemming/paths.py +245 -0
  56. lemming/paths_test.py +186 -0
  57. lemming/persistence.py +179 -0
  58. lemming/persistence_test.py +150 -0
  59. lemming/prompts/hooks/readability.md +42 -0
  60. lemming/prompts/hooks/roadmap.md +61 -0
  61. lemming/prompts/hooks/testing.md +44 -0
  62. lemming/prompts/taskrunner.md +69 -0
  63. lemming/prompts.py +354 -0
  64. lemming/prompts_test.py +421 -0
  65. lemming/providers.py +190 -0
  66. lemming/providers_test.py +73 -0
  67. lemming/runner.py +327 -0
  68. lemming/runner_test.py +378 -0
  69. lemming/tasks/__init__.py +75 -0
  70. lemming/tasks/lifecycle.py +331 -0
  71. lemming/tasks/lifecycle_test.py +312 -0
  72. lemming/tasks/operations.py +258 -0
  73. lemming/tasks/operations_test.py +172 -0
  74. lemming/tasks/progress.py +29 -0
  75. lemming/tasks/progress_test.py +22 -0
  76. lemming/tasks/queries.py +128 -0
  77. lemming/tasks/queries_test.py +233 -0
  78. lemming/web/dashboard.spec.js +350 -0
  79. lemming/web/dashboard.test.js +998 -0
  80. lemming/web/favicon.js +40 -0
  81. lemming/web/favicon.spec.js +242 -0
  82. lemming/web/files.html +375 -0
  83. lemming/web/files.spec.js +97 -0
  84. lemming/web/index.html +983 -0
  85. lemming/web/index.js +753 -0
  86. lemming/web/logs.html +358 -0
  87. lemming/web/logs.test.js +195 -0
  88. lemming/web/mancha.js +58 -0
  89. lemming/web/screenshots.spec.js +328 -0
  90. lemming_cli-0.1.0.dist-info/METADATA +314 -0
  91. lemming_cli-0.1.0.dist-info/RECORD +94 -0
  92. lemming_cli-0.1.0.dist-info/WHEEL +4 -0
  93. lemming_cli-0.1.0.dist-info/entry_points.txt +2 -0
  94. 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()
@@ -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