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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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,2 @@
1
+ [console_scripts]
2
+ loopsmith = loopsmith_cli.__main__:main
@@ -0,0 +1 @@
1
+ loopsmith_cli