loopsmith-cli 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.
- loopsmith_cli-0.1.1/PKG-INFO +54 -0
- loopsmith_cli-0.1.1/README.md +32 -0
- loopsmith_cli-0.1.1/pyproject.toml +37 -0
- loopsmith_cli-0.1.1/setup.cfg +4 -0
- loopsmith_cli-0.1.1/src/loopsmith_cli/__init__.py +144 -0
- loopsmith_cli-0.1.1/src/loopsmith_cli/__main__.py +40 -0
- loopsmith_cli-0.1.1/src/loopsmith_cli.egg-info/PKG-INFO +54 -0
- loopsmith_cli-0.1.1/src/loopsmith_cli.egg-info/SOURCES.txt +9 -0
- loopsmith_cli-0.1.1/src/loopsmith_cli.egg-info/dependency_links.txt +1 -0
- loopsmith_cli-0.1.1/src/loopsmith_cli.egg-info/entry_points.txt +2 -0
- loopsmith_cli-0.1.1/src/loopsmith_cli.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loopsmith-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Self-evolving agent loops behind a deterministic verification gate. Installs the prebuilt Rust binary as `loopsmith`.
|
|
5
|
+
Author-email: bitphill <bitphill@tuta.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bitphill/loopsmith
|
|
8
|
+
Project-URL: Repository, https://github.com/bitphill/loopsmith
|
|
9
|
+
Project-URL: Changelog, https://github.com/bitphill/loopsmith/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/bitphill/loopsmith/issues
|
|
11
|
+
Keywords: agent,loop,llm,orchestration,cli,rust
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Programming Language :: Rust
|
|
19
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# loopsmith-cli
|
|
24
|
+
|
|
25
|
+
Self-evolving agent loops. The gate is code, so "done" cannot be argued.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install loopsmith-cli
|
|
29
|
+
loopsmith doctor
|
|
30
|
+
loopsmith new --path ~/loops/my-loop --purpose "keep the module simple"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This package downloads the prebuilt Rust binary for your platform on first run,
|
|
34
|
+
from the matching
|
|
35
|
+
[GitHub release](https://github.com/bitphill/loopsmith/releases), and verifies it
|
|
36
|
+
against the release's published `SHA256SUMS` before executing it. The installed
|
|
37
|
+
command is `loopsmith`.
|
|
38
|
+
|
|
39
|
+
Prebuilt for Linux (x86_64 glibc and musl, aarch64), macOS (x86_64 and Apple
|
|
40
|
+
silicon), and Windows (x86_64). Anywhere else, build from source:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cargo install loopsmith
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The distribution is named `loopsmith-cli` because `loopsmith` on PyPI was already
|
|
47
|
+
registered by an unrelated project. The command is `loopsmith` regardless.
|
|
48
|
+
|
|
49
|
+
The download happens on first run rather than during `pip install`, so an image
|
|
50
|
+
that is built without a network and run with one still works.
|
|
51
|
+
|
|
52
|
+
Full documentation: <https://github.com/bitphill/loopsmith>
|
|
53
|
+
|
|
54
|
+
MIT licensed.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# loopsmith-cli
|
|
2
|
+
|
|
3
|
+
Self-evolving agent loops. The gate is code, so "done" cannot be argued.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install loopsmith-cli
|
|
7
|
+
loopsmith doctor
|
|
8
|
+
loopsmith new --path ~/loops/my-loop --purpose "keep the module simple"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This package downloads the prebuilt Rust binary for your platform on first run,
|
|
12
|
+
from the matching
|
|
13
|
+
[GitHub release](https://github.com/bitphill/loopsmith/releases), and verifies it
|
|
14
|
+
against the release's published `SHA256SUMS` before executing it. The installed
|
|
15
|
+
command is `loopsmith`.
|
|
16
|
+
|
|
17
|
+
Prebuilt for Linux (x86_64 glibc and musl, aarch64), macOS (x86_64 and Apple
|
|
18
|
+
silicon), and Windows (x86_64). Anywhere else, build from source:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cargo install loopsmith
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The distribution is named `loopsmith-cli` because `loopsmith` on PyPI was already
|
|
25
|
+
registered by an unrelated project. The command is `loopsmith` regardless.
|
|
26
|
+
|
|
27
|
+
The download happens on first run rather than during `pip install`, so an image
|
|
28
|
+
that is built without a network and run with one still works.
|
|
29
|
+
|
|
30
|
+
Full documentation: <https://github.com/bitphill/loopsmith>
|
|
31
|
+
|
|
32
|
+
MIT licensed.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
# `loopsmith` on PyPI was already registered by an unrelated project, so the
|
|
7
|
+
# distribution is `loopsmith-cli`. The installed command is `loopsmith`.
|
|
8
|
+
name = "loopsmith-cli"
|
|
9
|
+
version = "0.1.1"
|
|
10
|
+
description = "Self-evolving agent loops behind a deterministic verification gate. Installs the prebuilt Rust binary as `loopsmith`."
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
authors = [{ name = "bitphill", email = "bitphill@tuta.com" }]
|
|
15
|
+
keywords = ["agent", "loop", "llm", "orchestration", "cli", "rust"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Operating System :: MacOS :: MacOS X",
|
|
22
|
+
"Operating System :: Microsoft :: Windows",
|
|
23
|
+
"Programming Language :: Rust",
|
|
24
|
+
"Topic :: Software Development :: Build Tools",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/bitphill/loopsmith"
|
|
29
|
+
Repository = "https://github.com/bitphill/loopsmith"
|
|
30
|
+
Changelog = "https://github.com/bitphill/loopsmith/blob/main/CHANGELOG.md"
|
|
31
|
+
Issues = "https://github.com/bitphill/loopsmith/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
loopsmith = "loopsmith_cli.__main__:main"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["src"]
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Launcher for the prebuilt ``loopsmith`` binary.
|
|
2
|
+
|
|
3
|
+
The binary is fetched on first run rather than at install time. A wheel that
|
|
4
|
+
downloads during ``pip install`` breaks in every environment that installs with
|
|
5
|
+
no network and then runs with one — CI images, Docker build stages, and locked-down
|
|
6
|
+
build hosts — and the failure surfaces as an install error for a package the user
|
|
7
|
+
has not tried to use yet.
|
|
8
|
+
|
|
9
|
+
Every download is verified against the release's published ``SHA256SUMS`` before
|
|
10
|
+
it is executed. Fetching a binary and running it unverified is a supply-chain
|
|
11
|
+
hole with a progress bar.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import hashlib
|
|
17
|
+
import os
|
|
18
|
+
import platform
|
|
19
|
+
import shutil
|
|
20
|
+
import stat
|
|
21
|
+
import sys
|
|
22
|
+
import tarfile
|
|
23
|
+
import tempfile
|
|
24
|
+
import urllib.request
|
|
25
|
+
import zipfile
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.1"
|
|
29
|
+
|
|
30
|
+
REPO = "bitphill/loopsmith"
|
|
31
|
+
_RELEASE_BASE = f"https://github.com/{REPO}/releases/download/v{__version__}"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ResolveError(RuntimeError):
|
|
35
|
+
"""This host has no prebuilt binary, or one could not be verified."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _target() -> str:
|
|
39
|
+
"""The Rust target triple for this interpreter's host.
|
|
40
|
+
|
|
41
|
+
``platform.machine()`` rather than ``platform.processor()``: the latter is
|
|
42
|
+
empty on most Linux distributions and returns marketing strings on Windows.
|
|
43
|
+
"""
|
|
44
|
+
system = platform.system().lower()
|
|
45
|
+
machine = platform.machine().lower()
|
|
46
|
+
arm = machine in ("arm64", "aarch64")
|
|
47
|
+
intel = machine in ("x86_64", "amd64")
|
|
48
|
+
|
|
49
|
+
if system == "linux":
|
|
50
|
+
if intel:
|
|
51
|
+
# musl and glibc need different binaries, and a glibc build on musl
|
|
52
|
+
# dies with a bare "not found" that names no missing library.
|
|
53
|
+
libc, _ = platform.libc_ver()
|
|
54
|
+
flavour = "gnu" if libc else "musl"
|
|
55
|
+
return f"x86_64-unknown-linux-{flavour}"
|
|
56
|
+
if arm:
|
|
57
|
+
return "aarch64-unknown-linux-gnu"
|
|
58
|
+
elif system == "darwin":
|
|
59
|
+
if arm:
|
|
60
|
+
return "aarch64-apple-darwin"
|
|
61
|
+
if intel:
|
|
62
|
+
return "x86_64-apple-darwin"
|
|
63
|
+
elif system == "windows" and intel:
|
|
64
|
+
return "x86_64-pc-windows-msvc"
|
|
65
|
+
|
|
66
|
+
raise ResolveError(
|
|
67
|
+
f"no prebuilt loopsmith for {system}/{machine} at v{__version__}.\n"
|
|
68
|
+
"Build from source instead: cargo install loopsmith"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def cache_dir() -> Path:
|
|
73
|
+
"""Where the downloaded binary is kept, versioned so an upgrade re-fetches."""
|
|
74
|
+
env = os.environ.get("LOOPSMITH_HOME")
|
|
75
|
+
root = Path(env) if env else Path.home() / ".loopsmith"
|
|
76
|
+
return root / "bin" / __version__
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _read(url: str) -> bytes:
|
|
80
|
+
with urllib.request.urlopen(url, timeout=120) as response: # noqa: S310
|
|
81
|
+
return response.read()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _expected_digest(asset: str) -> str:
|
|
85
|
+
sums = _read(f"{_RELEASE_BASE}/SHA256SUMS").decode("utf-8")
|
|
86
|
+
for line in sums.splitlines():
|
|
87
|
+
line = line.strip()
|
|
88
|
+
if line.endswith(asset):
|
|
89
|
+
return line.split()[0]
|
|
90
|
+
raise ResolveError(f"{asset} is not listed in the release SHA256SUMS")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _extract(archive: Path, into: Path, member: str) -> Path:
|
|
94
|
+
into.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
if archive.suffix == ".zip":
|
|
96
|
+
with zipfile.ZipFile(archive) as zf:
|
|
97
|
+
zf.extract(member, path=into)
|
|
98
|
+
else:
|
|
99
|
+
with tarfile.open(archive, "r:gz") as tf:
|
|
100
|
+
# One named member, not extractall: an archive is untrusted input
|
|
101
|
+
# even when its checksum matched, and a path-traversing entry only
|
|
102
|
+
# needs one careless extraction.
|
|
103
|
+
info = tf.getmember(member)
|
|
104
|
+
if info.name != member or info.issym() or info.islnk():
|
|
105
|
+
raise ResolveError(f"unexpected entry {info.name!r} in {archive.name}")
|
|
106
|
+
tf.extract(info, path=into)
|
|
107
|
+
return into / member
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def ensure_binary() -> Path:
|
|
111
|
+
"""Path to a verified ``loopsmith``, downloading it once if needed."""
|
|
112
|
+
windows = platform.system().lower() == "windows"
|
|
113
|
+
name = "loopsmith.exe" if windows else "loopsmith"
|
|
114
|
+
destination = cache_dir() / name
|
|
115
|
+
if destination.exists():
|
|
116
|
+
return destination
|
|
117
|
+
|
|
118
|
+
# A `loopsmith` already on PATH beats downloading a second copy — someone who
|
|
119
|
+
# ran `cargo install loopsmith` should not silently get two binaries.
|
|
120
|
+
existing = shutil.which("loopsmith")
|
|
121
|
+
if existing and Path(existing).resolve() != destination.resolve():
|
|
122
|
+
return Path(existing)
|
|
123
|
+
|
|
124
|
+
target = _target()
|
|
125
|
+
asset = f"loopsmith-v{__version__}-{target}.{'zip' if windows else 'tar.gz'}"
|
|
126
|
+
want = _expected_digest(asset)
|
|
127
|
+
payload = _read(f"{_RELEASE_BASE}/{asset}")
|
|
128
|
+
|
|
129
|
+
got = hashlib.sha256(payload).hexdigest()
|
|
130
|
+
if got != want:
|
|
131
|
+
raise ResolveError(
|
|
132
|
+
f"checksum mismatch for {asset}: expected {want}, got {got}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
136
|
+
archive = Path(tmp) / asset
|
|
137
|
+
archive.write_bytes(payload)
|
|
138
|
+
extracted = _extract(archive, Path(tmp) / "unpacked", name)
|
|
139
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
140
|
+
shutil.move(str(extracted), str(destination))
|
|
141
|
+
|
|
142
|
+
destination.chmod(destination.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
|
143
|
+
print(f"[loopsmith] installed {destination}", file=sys.stderr)
|
|
144
|
+
return destination
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""``loopsmith`` console entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from . import ResolveError, ensure_binary
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> int:
|
|
13
|
+
try:
|
|
14
|
+
exe = ensure_binary()
|
|
15
|
+
except ResolveError as e:
|
|
16
|
+
print(f"[loopsmith] {e}", file=sys.stderr)
|
|
17
|
+
return 127
|
|
18
|
+
except OSError as e:
|
|
19
|
+
print(f"[loopsmith] could not fetch the binary: {e}", file=sys.stderr)
|
|
20
|
+
print("[loopsmith] build from source instead: cargo install loopsmith", file=sys.stderr)
|
|
21
|
+
return 127
|
|
22
|
+
|
|
23
|
+
argv = [str(exe), *sys.argv[1:]]
|
|
24
|
+
|
|
25
|
+
# On POSIX, replace this process rather than wrapping it. loopsmith is
|
|
26
|
+
# long-running and interactive, so a Python parent adds a process that has to
|
|
27
|
+
# forward signals correctly and gets it wrong at least once. `execv` makes the
|
|
28
|
+
# shell's Ctrl-C reach the real binary, and its exit code arrive unchanged.
|
|
29
|
+
if os.name != "nt":
|
|
30
|
+
os.execv(str(exe), argv) # noqa: S606 — never returns
|
|
31
|
+
|
|
32
|
+
# Windows has no exec that replaces the process, so wrap and propagate.
|
|
33
|
+
try:
|
|
34
|
+
return subprocess.call(argv)
|
|
35
|
+
except KeyboardInterrupt:
|
|
36
|
+
return 130
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loopsmith-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Self-evolving agent loops behind a deterministic verification gate. Installs the prebuilt Rust binary as `loopsmith`.
|
|
5
|
+
Author-email: bitphill <bitphill@tuta.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bitphill/loopsmith
|
|
8
|
+
Project-URL: Repository, https://github.com/bitphill/loopsmith
|
|
9
|
+
Project-URL: Changelog, https://github.com/bitphill/loopsmith/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/bitphill/loopsmith/issues
|
|
11
|
+
Keywords: agent,loop,llm,orchestration,cli,rust
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Programming Language :: Rust
|
|
19
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# loopsmith-cli
|
|
24
|
+
|
|
25
|
+
Self-evolving agent loops. The gate is code, so "done" cannot be argued.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install loopsmith-cli
|
|
29
|
+
loopsmith doctor
|
|
30
|
+
loopsmith new --path ~/loops/my-loop --purpose "keep the module simple"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This package downloads the prebuilt Rust binary for your platform on first run,
|
|
34
|
+
from the matching
|
|
35
|
+
[GitHub release](https://github.com/bitphill/loopsmith/releases), and verifies it
|
|
36
|
+
against the release's published `SHA256SUMS` before executing it. The installed
|
|
37
|
+
command is `loopsmith`.
|
|
38
|
+
|
|
39
|
+
Prebuilt for Linux (x86_64 glibc and musl, aarch64), macOS (x86_64 and Apple
|
|
40
|
+
silicon), and Windows (x86_64). Anywhere else, build from source:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cargo install loopsmith
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The distribution is named `loopsmith-cli` because `loopsmith` on PyPI was already
|
|
47
|
+
registered by an unrelated project. The command is `loopsmith` regardless.
|
|
48
|
+
|
|
49
|
+
The download happens on first run rather than during `pip install`, so an image
|
|
50
|
+
that is built without a network and run with one still works.
|
|
51
|
+
|
|
52
|
+
Full documentation: <https://github.com/bitphill/loopsmith>
|
|
53
|
+
|
|
54
|
+
MIT licensed.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/loopsmith_cli/__init__.py
|
|
4
|
+
src/loopsmith_cli/__main__.py
|
|
5
|
+
src/loopsmith_cli.egg-info/PKG-INFO
|
|
6
|
+
src/loopsmith_cli.egg-info/SOURCES.txt
|
|
7
|
+
src/loopsmith_cli.egg-info/dependency_links.txt
|
|
8
|
+
src/loopsmith_cli.egg-info/entry_points.txt
|
|
9
|
+
src/loopsmith_cli.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
loopsmith_cli
|