loopgate 0.1.0__tar.gz → 0.1.1__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.
Files changed (44) hide show
  1. {loopgate-0.1.0 → loopgate-0.1.1}/PKG-INFO +2 -3
  2. {loopgate-0.1.0 → loopgate-0.1.1}/harness/cli.py +36 -33
  3. {loopgate-0.1.0 → loopgate-0.1.1}/harness/config.py +15 -24
  4. {loopgate-0.1.0 → loopgate-0.1.1}/harness/temp.pyproject.toml +1 -1
  5. {loopgate-0.1.0 → loopgate-0.1.1}/harness/tests/test_cli.py +181 -101
  6. {loopgate-0.1.0 → loopgate-0.1.1}/harness/tests/test_gate.py +1 -1
  7. {loopgate-0.1.0 → loopgate-0.1.1}/pyproject.toml +6 -6
  8. {loopgate-0.1.0 → loopgate-0.1.1}/tests/preferences/test_preferences.py +234 -131
  9. loopgate-0.1.0/tests/preferences/test_preferences_properties.py +0 -245
  10. {loopgate-0.1.0 → loopgate-0.1.1}/.githooks/_resolve +0 -0
  11. {loopgate-0.1.0 → loopgate-0.1.1}/.githooks/hoist +0 -0
  12. {loopgate-0.1.0 → loopgate-0.1.1}/.githooks/pre-commit +0 -0
  13. {loopgate-0.1.0 → loopgate-0.1.1}/.githooks/pre-push +0 -0
  14. {loopgate-0.1.0 → loopgate-0.1.1}/.githooks/prepare-commit-msg +0 -0
  15. {loopgate-0.1.0 → loopgate-0.1.1}/.gitignore +0 -0
  16. {loopgate-0.1.0 → loopgate-0.1.1}/LICENSE +0 -0
  17. {loopgate-0.1.0 → loopgate-0.1.1}/README.md +0 -0
  18. {loopgate-0.1.0 → loopgate-0.1.1}/docs/PROJECT_STATUS.md +0 -0
  19. {loopgate-0.1.0 → loopgate-0.1.1}/docs/PROMPT.md +0 -0
  20. {loopgate-0.1.0 → loopgate-0.1.1}/docs/plan.md +0 -0
  21. {loopgate-0.1.0 → loopgate-0.1.1}/docs/specs/another_spec.md +0 -0
  22. {loopgate-0.1.0 → loopgate-0.1.1}/docs/specs/base.md +0 -0
  23. {loopgate-0.1.0 → loopgate-0.1.1}/harness/__init__.py +0 -0
  24. {loopgate-0.1.0 → loopgate-0.1.1}/harness/gate.py +0 -0
  25. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/PROMPT.md +0 -0
  26. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/README.md +0 -0
  27. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/index.html +0 -0
  28. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/package-lock.json +0 -0
  29. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/package.json +0 -0
  30. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/quiz.js +0 -0
  31. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/specs/quiz.md +0 -0
  32. {loopgate-0.1.0 → loopgate-0.1.1}/harness/js-scaffold/test.js +0 -0
  33. {loopgate-0.1.0 → loopgate-0.1.1}/harness/ralph.ps1 +0 -0
  34. {loopgate-0.1.0 → loopgate-0.1.1}/harness/ralph.sh +0 -0
  35. {loopgate-0.1.0 → loopgate-0.1.1}/harness/tests/conftest.py +0 -0
  36. {loopgate-0.1.0 → loopgate-0.1.1}/harness/tests/test_init_fixtures.py +0 -0
  37. {loopgate-0.1.0 → loopgate-0.1.1}/harness/tests/test_properties.py +0 -0
  38. {loopgate-0.1.0 → loopgate-0.1.1}/harness/tests/test_ralph.py +0 -0
  39. {loopgate-0.1.0 → loopgate-0.1.1}/harness/tests/test_ralph_ps1.py +0 -0
  40. {loopgate-0.1.0 → loopgate-0.1.1}/mutation/check_mutmut.py +0 -0
  41. {loopgate-0.1.0 → loopgate-0.1.1}/preferences/__init__.py +0 -0
  42. {loopgate-0.1.0 → loopgate-0.1.1}/preferences/preferences.py +0 -0
  43. {loopgate-0.1.0 → loopgate-0.1.1}/tests/mutation/mutmut-cicd-stats.json +0 -0
  44. {loopgate-0.1.0 → loopgate-0.1.1}/tests/mutation/test_check_mutmut.py +0 -0
@@ -1,14 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: loopgate
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Gated, harnessed, autonomy loop with quality checks.
5
5
  Author: rxdt
6
- License: MIT
6
+ License-Expression: MIT
7
7
  License-File: LICENSE
8
8
  Keywords: agent,autonomy,claude,codex,harness,llm,loop,scaffold
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
11
  Classifier: Programming Language :: Python :: 3 :: Only
13
12
  Classifier: Programming Language :: Python :: 3.10
14
13
  Classifier: Programming Language :: Python :: 3.11
@@ -161,8 +161,9 @@ def info() -> None:
161
161
  def status() -> None:
162
162
  """Count run logs and point at the newest one."""
163
163
  runs = REPO_ROOT / "scratchpad" / "runs"
164
- logs = sorted(runs.glob("*.jsonl")) if runs.is_dir() else [""]
165
- secho(f"{len(logs)} run log(s) in {runs}\nnewest: {logs[-1]}", fg=colors.CYAN, bold=True)
164
+ logs = sorted(runs.glob("*.jsonl"), reverse=True) if runs.is_dir() else []
165
+ newest = "\n".join(str(log) for log in logs[:3]) if logs else ""
166
+ secho(f"{len(logs)} run log(s) in {runs}\nnewest:\n{newest}", fg=30)
166
167
 
167
168
 
168
169
  def cleanup(cwd: Path) -> bool:
@@ -195,7 +196,7 @@ def cleanup(cwd: Path) -> bool:
195
196
  )
196
197
  def install() -> None:
197
198
  """Used by template cloned from Github. Syncs dependencies, and activates the git hooks."""
198
- rprint("\n[cyan2]installing dependencies[/cyan2]")
199
+ rprint("\n[cyan2]installing dependencies[/]")
199
200
  env_bin = infer_env_manager_and_install()
200
201
  cleanup(REPO_ROOT)
201
202
  setup_git_hooks(env_bin)
@@ -340,21 +341,21 @@ def init() -> None:
340
341
  ):
341
342
  secho("Run `harness init` to configure loopgate", fg=colors.MAGENTA, bold=True)
342
343
  raise Exit(code=0)
343
- if write_harness_config():
344
- confirm(style("\n2. Can we wire githooks so quality checks run?", fg=10), default=True, abort=True)
344
+ wired = write_harness_config()
345
345
  hoisted = hoist()
346
346
  hooks = setup_git_hooks(Path(sys.executable).parent)
347
347
  timeout = check_for_timeout_and_prompt() or IS_WINDOWS
348
348
  agents = configure_agents()
349
349
  rprint(
350
- f"\n[bold cyan2]RESULT:[/]\nfiles added: {hoisted}\ngit hooks available via path: {hooks}"
351
- f"\ntimeout-ready: {timeout}\nagents configured: {agents}"
352
- f"\n[bold cyan2]Can likely run loops: [/]{bool(hoisted and hooks and timeout)}\n"
353
- "\n[italic]Ensure your environemnt is activated to use the `harness run` command[/]\n"
350
+ f"\n[bold cyan2]RESULT:[/]\ngit hooks available via path: {hooks}\ntimeout-ready: {timeout}"
351
+ f"\nfiles added: {hoisted}\nagents configured: {agents}"
352
+ f"\nHarness commands in `pyproject.toml to run as checks`:\n{set(wired)}\n"
353
+ f"\n[bold cyan2]Can likely run loops with checks: [/]{bool(wired and hoisted and hooks and timeout)}\n"
354
+ "\n[italic]To loop with checks using `harness run` command, ensure your environemnt is activated[/]\n"
354
355
  )
355
356
 
356
357
 
357
- def write_harness_config() -> bool:
358
+ def write_harness_config() -> set[str]:
358
359
  """Takes user's configs and creates a pyproject.toml or appends to an existing pyproject.toml."""
359
360
  pyproject_path = REPO_ROOT / "pyproject.toml"
360
361
  user_pyproject: TOMLDocument = document()
@@ -362,21 +363,21 @@ def write_harness_config() -> bool:
362
363
  if pyproject_path.is_file():
363
364
  user_pyproject: TOMLDocument = parse(pyproject_path.read_text(encoding="utf-8"))
364
365
  user_pyproject_tools: dict[str, Any] = user_pyproject.setdefault("tool", table())
365
- user_harness = user_pyproject_tools.get("harness", {})
366
- checks = user_harness.get("gate", {})
367
- if checks and checks.get("test"):
368
- confirm(style("Seems loopgate may be wired already. Continue?", fg=10), default=True, abort=True)
369
366
  template: Path = Path(__file__).resolve().with_name("temp.pyproject.toml")
370
367
  template_contents: TOMLDocument = parse(template.read_text(encoding="utf-8"))
371
- for t in template_contents["tool"]:
368
+ template_tools = template_contents["tool"]
369
+ for t in template_tools:
372
370
  if t in user_pyproject_tools:
373
- template_contents["tool"][t] = deepcopy(user_pyproject_tools[t])
371
+ template_tools[t] = deepcopy(user_pyproject_tools[t])
374
372
  contents = ConfigParser(interpolation=None)
375
373
  contents.read([(REPO_ROOT / "tox.ini"), (REPO_ROOT / "setup.cfg")], encoding="utf-8")
376
374
  section_names = contents.sections()
377
375
  inspect_configs(section_names, user_pyproject_tools, deepcopy(CATEGORIES), template_contents["tool"])
378
- user_pyproject.setdefault("tool", {}).update(template_contents["tool"])
379
- return bool(pyproject_path.write_text(dumps(user_pyproject), encoding="utf-8"))
376
+ user_pyproject.setdefault("tool", {}).update(template_tools)
377
+ pyproject_path.write_text(dumps(user_pyproject), encoding="utf-8")
378
+ updated_pyproject: TOMLDocument = parse(pyproject_path.read_text(encoding="utf-8"))
379
+ harness: dict[str, Any] = updated_pyproject.setdefault("tool", table()).get("harness", {})
380
+ return set(harness.get("preflight", {}) | harness.get("gate", {}))
380
381
 
381
382
 
382
383
  def find_configured_command(
@@ -412,10 +413,7 @@ def find_configured_command(
412
413
 
413
414
 
414
415
  def inspect_configs(
415
- section_names: list[str],
416
- user_pyproject_tools: dict[str, Any],
417
- categories: dict[str, str],
418
- template: dict[str, Any],
416
+ section_names: list[str], user_pyproject_tools: dict[str, Any], categories: dict[str, str], template: dict[str, Any]
419
417
  ) -> None:
420
418
  """For each tool in a pre-built internal map, see if user has the tool installed and configured.
421
419
  Args:
@@ -453,35 +451,40 @@ def hoist() -> bool:
453
451
  if not (ASSETS["docs"][0].is_dir() and ASSETS["githooks"][0].is_dir()):
454
452
  rprint("Harness is missing required assets: `docs/` and `githooks/`")
455
453
  return False
454
+ confirm(style("\n2. Can we wire githooks so quality checks run?", fg=10), default=True, abort=True)
455
+ repo_root = REPO_ROOT
456
+ ASSETS["githooks"][1].mkdir(parents=True, exist_ok=True)
457
+ hoist_script: str = (ASSETS["githooks"][0] / "hoist").as_posix()
458
+ run_git(["-c", 'alias.loopgate-hoist=!f() { sh "$1"; }; f', "loopgate-hoist", hoist_script], REPO_ROOT)
459
+ console.print("[green]\n3. Ran script to make `.githooks` executable[/]\n")
456
460
  rprint(
457
- "\n[bold yellow]We will need to add these files[/]\n* Git hooks are what ensure quality checks run"
458
- "\n* Mutation tests promote good tests.\n* `preferences` allow for checks beyond what tooling catches"
459
- "and demonstrate Hypothesis property tests\n* `docs` contain the instructions and memory for loops "
461
+ "\n[bold yellow]We will need to add these files[/]\n* `.githooks` are what ensure quality checks run"
462
+ "\n* Mutation tests promote better tests. Docs and code for example test at `mutation/` and `tests/mutation`"
463
+ "\n* `preferences/` allows for checks beyond what tooling catches"
464
+ "and demonstrates Hypothesis property tests\n* `docs` contain the instructions and memory for loops "
460
465
  "\n*`scratchpad/` allows local agent use and contains a `runs/` directory for logs."
461
466
  )
462
467
  confirm(
463
468
  style(
464
- "3. Confirm, loopgate can add those files? Pre-existing files in the expected paths will remain "
469
+ "\n4. Confirm, loopgate can add those files? Pre-existing files in the expected paths will remain "
465
470
  "and loopgate will skip adding them.",
466
471
  fg=10,
467
472
  ),
468
473
  default=True,
469
474
  abort=True,
470
475
  )
471
- repo_root = REPO_ROOT
472
- ASSETS["githooks"][1].mkdir(parents=True, exist_ok=True)
473
- hoist_script: str = (ASSETS["githooks"][0] / "hoist").as_posix()
474
- run_git(["-c", 'alias.loopgate-hoist=!f() { sh "$1"; }; f', "loopgate-hoist", hoist_script], REPO_ROOT)
475
- console.print(f"[green]\n4. Ran {hoist_script}[/]\n")
476
476
  for key, paths in ASSETS.items():
477
477
  source, destination = paths
478
- for source_path in source.rglob("*"):
479
- destination_path = destination / source_path.relative_to(source)
478
+ source_paths = source.rglob("*") if source.is_dir() else (source,)
479
+ for source_path in source_paths:
480
+ destination_path = destination / source_path.relative_to(source) if source.is_dir() else destination
480
481
  if source_path.is_dir():
481
482
  destination_path.mkdir(parents=True, exist_ok=True)
482
483
  elif not destination_path.exists():
483
484
  destination_path.parent.mkdir(parents=True, exist_ok=True)
484
485
  copy2(source_path, destination_path)
486
+ else:
487
+ rprint(f"Skipped existing `{destination_path}` during copy")
485
488
  rprint(f"`{key}/` exists {destination.exists()}")
486
489
  (repo_root / "scratchpad" / "runs").mkdir(parents=True, exist_ok=True)
487
490
  (repo_root / "scratchpad" / "runs" / ".gitkeep").touch()
@@ -2,14 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from importlib.metadata import distribution, packages_distributions
5
+ from importlib.metadata import distribution
6
6
  from pathlib import Path
7
7
  from typing import Any
8
8
 
9
9
  from harness.gate import gates
10
10
 
11
- distribution_name = packages_distributions()["harness"][0]
12
- site_packages = Path(str(distribution(distribution_name).locate_file("")))
11
+ site_packages = Path(str(distribution("loopgate").locate_file("")))
13
12
  package_root = site_packages / "harness"
14
13
  repo_root = gates().repo_root
15
14
 
@@ -78,11 +77,7 @@ TOOLS: dict[str, dict[str, Any]] = {
78
77
  "args": gates().commit_checks["ruff_lint"],
79
78
  },
80
79
  "black": {"category": "format", "pyproject": ["black"], "args": ["black", "--check", "."]},
81
- "coverage": {
82
- "category": "test",
83
- "filenames": [".coveragerc", ".coveragerc.toml"],
84
- "pyproject": ["coverage"],
85
- },
80
+ "coverage": {"category": "test", "filenames": [".coveragerc", ".coveragerc.toml"], "pyproject": ["coverage"]},
86
81
  "flake8": {"category": "lint", "filenames": [".flake8"], "args": ["flake8", "."]},
87
82
  "hypothesis": {"category": "test", "pyproject": ["hypothesis"]},
88
83
  "lint": {"category": "lint", "pyproject": ["lint"]},
@@ -104,12 +99,7 @@ TOOLS: dict[str, dict[str, Any]] = {
104
99
  "pyproject": ["pylint"],
105
100
  "args": gates().commit_checks["pylint"],
106
101
  },
107
- "mypy": {
108
- "category": "types",
109
- "filenames": ["mypy.ini", ".mypy.ini"],
110
- "pyproject": ["mypy"],
111
- "args": ["mypy", "."],
112
- },
102
+ "mypy": {"category": "types", "filenames": ["mypy.ini", ".mypy.ini"], "pyproject": ["mypy"], "args": ["mypy", "."]},
113
103
  "mutmut": {"category": "test", "pyproject": ["mutmut"]},
114
104
  "radon": {
115
105
  "category": "complexity",
@@ -173,12 +163,7 @@ TOOLS: dict[str, dict[str, Any]] = {
173
163
  "filenames": [".xenon.yml"],
174
164
  "args": ["xenon", "--max-absolute", "B", "--max-modules", "A", "--max-average", "A", "."],
175
165
  },
176
- "zuban": {
177
- "category": "types",
178
- "filenames": [".zuban.toml"],
179
- "pyproject": ["zuban"],
180
- "args": ["zuban", "check"],
181
- },
166
+ "zuban": {"category": "types", "filenames": [".zuban.toml"], "pyproject": ["zuban"], "args": ["zuban", "check"]},
182
167
  }
183
168
 
184
169
  CLAUDE_RULES: set[str] = {
@@ -196,8 +181,14 @@ CLAUDE_RULES: set[str] = {
196
181
  ASSETS: dict[str, tuple[Path, Path]] = {
197
182
  "docs": (package_root / "docs", repo_root / "docs"),
198
183
  "githooks": (package_root / ".githooks", repo_root / ".githooks"),
199
- "preferences": (site_packages / "preferences", repo_root / "preferences"),
200
- "mutation": (site_packages / "mutation", repo_root / "mutation"),
201
- "pref_tests": (package_root / "tests/preferences", repo_root / "tests/preferences"),
202
- "mutation_tests": (package_root / "tests/mutation", repo_root / "tests/mutation"),
184
+ "preferences": (site_packages / "preferences/preferences.py", repo_root / "preferences/preferences.py"),
185
+ "mutation": (site_packages / "mutation/check_mutmut.py", repo_root / "mutation/check_mutmut.py"),
186
+ "tests/preferences": (
187
+ package_root / "tests/preferences/test_preferences.py",
188
+ repo_root / "tests/preferences/test_preferences.py",
189
+ ),
190
+ "tests/mutation": (
191
+ package_root / "tests/mutation/test_check_mutmut.py",
192
+ repo_root / "tests/mutation/test_check_mutmut.py",
193
+ ),
203
194
  }
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "loopgate"
3
3
  requires-python = ">=3.10"
4
- version = "0.1.0"
4
+ version = "0.1.1"
5
5
  [project.scripts]
6
6
  harness = "harness.cli:main" # creates the executable `harness`
7
7
  [build-system]