agentic-coding-harness 0.7.1__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.
@@ -0,0 +1,10 @@
1
+ """agentic-coding-harness: run/watch/meter coding agents.
2
+
3
+ Python wrapper that locates a JavaScript runtime (Bun preferred, then
4
+ Node.js >= 18.19) and execs the vendored ach.mjs CLI bundle with full
5
+ argument passthrough.
6
+ """
7
+
8
+ __version__ = "0.7.1"
9
+
10
+ __all__ = ["__version__"]
@@ -0,0 +1,126 @@
1
+ """Console-script entry point: locate a JS runtime and exec the vendored ach.mjs.
2
+
3
+ Stdlib only. Prefers Bun, then Node >= 18.19. The runtime may be forced via
4
+ the AGENTIC_CODING_HARNESS_RUNTIME environment variable. On POSIX we execv so
5
+ stdio, signals, and exit codes pass through untouched; on Windows we fall back
6
+ to subprocess.call.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import shutil
13
+ import subprocess
14
+ import sys
15
+
16
+ _PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__))
17
+ _BUNDLE = os.path.join(_PACKAGE_DIR, "_vendor", "ach.mjs")
18
+ _RUNTIME_ENV = "AGENTIC_CODING_HARNESS_RUNTIME"
19
+
20
+ _COMMON_DIRS = (
21
+ "/opt/homebrew/bin",
22
+ "/usr/local/bin",
23
+ os.path.expanduser("~/.bun/bin"),
24
+ os.path.expanduser("~/.volta/bin"),
25
+ )
26
+
27
+ _MISSING_RUNTIME_MESSAGE = """ach: no JavaScript runtime found.
28
+
29
+ agentic-coding-harness needs Bun or Node.js (>= 18.19) to run.
30
+
31
+ Install one of:
32
+ curl -fsSL https://bun.sh/install | bash # Bun (recommended)
33
+ https://nodejs.org/en/download # Node.js >= 18.19
34
+
35
+ Or point {env} at an existing runtime binary, e.g.:
36
+ export {env}=/usr/local/bin/node
37
+ """.format(env=_RUNTIME_ENV)
38
+
39
+
40
+ def _candidate_paths(name: str):
41
+ found = shutil.which(name)
42
+ if found:
43
+ yield found
44
+ seen = {found} if found else set()
45
+ for directory in _COMMON_DIRS:
46
+ candidate = os.path.join(directory, name)
47
+ if candidate not in seen and os.path.isfile(candidate) and os.access(candidate, os.X_OK):
48
+ seen.add(candidate)
49
+ yield candidate
50
+
51
+
52
+ def _node_major_minor(path: str):
53
+ """Return (major, minor) for a node binary, or None if it can't be probed."""
54
+ try:
55
+ out = subprocess.run(
56
+ [path, "--version"],
57
+ capture_output=True,
58
+ text=True,
59
+ timeout=10,
60
+ check=False,
61
+ )
62
+ except (OSError, subprocess.SubprocessError):
63
+ return None
64
+ version = (out.stdout or "").strip().lstrip("v")
65
+ if not version:
66
+ return None
67
+ parts = version.split(".")
68
+ try:
69
+ return int(parts[0]), int(parts[1])
70
+ except (ValueError, IndexError):
71
+ return None
72
+
73
+
74
+ def find_runtime(env=None):
75
+ """Return the path to a usable JS runtime, or None.
76
+
77
+ Honors AGENTIC_CODING_HARNESS_RUNTIME when set.
78
+ """
79
+ environ = env if env is not None else os.environ
80
+ override = environ.get(_RUNTIME_ENV)
81
+ if override:
82
+ if os.path.isfile(override) and os.access(override, os.X_OK):
83
+ return override
84
+ resolved = shutil.which(override)
85
+ if resolved:
86
+ return resolved
87
+ return None
88
+
89
+ for bun in _candidate_paths("bun"):
90
+ return bun
91
+
92
+ for node in _candidate_paths("node"):
93
+ version = _node_major_minor(node)
94
+ if version is None:
95
+ # Unprobeable (e.g. broken install) - skip rather than fail hard.
96
+ continue
97
+ major, minor = version
98
+ if (major, minor) >= (18, 19):
99
+ return node
100
+
101
+ return None
102
+
103
+
104
+ def main(argv=None):
105
+ runtime = find_runtime()
106
+ if runtime is None:
107
+ sys.stderr.write(_MISSING_RUNTIME_MESSAGE)
108
+ return 127
109
+ if not os.path.isfile(_BUNDLE):
110
+ sys.stderr.write(
111
+ "ach: vendored bundle missing ({}). The install is broken; "
112
+ "reinstall agentic-coding-harness.\n".format(_BUNDLE)
113
+ )
114
+ return 127
115
+
116
+ args = list(sys.argv[1:] if argv is None else argv)
117
+
118
+ if os.name == "posix":
119
+ os.execv(runtime, [runtime, _BUNDLE] + args) # never returns
120
+ return 127 # pragma: no cover - execv failed to replace us
121
+
122
+ return subprocess.call([runtime, _BUNDLE] + args)
123
+
124
+
125
+ if __name__ == "__main__":
126
+ sys.exit(main())