evals 2.3.0 → 2.5.0

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,439 @@
1
+ #!/bin/bash
2
+ # Arize Coding Harness Tracing — Thin shell router
3
+ #
4
+ # Handles Python discovery, repo clone/tarball, venv creation, and pip install.
5
+ # All harness-specific logic lives in tracing/<harness>/install.py.
6
+ #
7
+ # Usage:
8
+ # curl -sSL .../install.sh | bash -s -- claude [--with-skills] [--branch NAME]
9
+ # ./install.sh uninstall [<harness>]
10
+ # ./install.sh update
11
+
12
+ set -euo pipefail
13
+
14
+ REPO_URL="https://github.com/Arize-ai/coding-harness-tracing.git"
15
+ INSTALL_BRANCH="${ARIZE_INSTALL_BRANCH:-main}"
16
+ TARBALL_URL="https://github.com/Arize-ai/coding-harness-tracing/archive/refs/heads/${INSTALL_BRANCH}.tar.gz"
17
+ INSTALL_DIR="${HOME}/.arize/harness"
18
+ VENV_DIR="${INSTALL_DIR}/venv"
19
+ # When set, install from local wheels in this directory instead of fetching the
20
+ # repo. Lets a caller that already ships the wheels install with no network at
21
+ # all — and with no remote code execution for a permission layer to object to.
22
+ WHEEL_DIR="${ARIZE_WHEEL_DIR:-}"
23
+
24
+ # -- Terminal helpers --------------------------------------------------------
25
+ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
26
+ BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m'
27
+ [[ -n "${NO_COLOR:-}" ]] || [[ ! -t 1 ]] && { RED=""; GREEN=""; YELLOW=""; BLUE=""; BOLD=""; NC=""; }
28
+
29
+ info() { echo -e "${GREEN}[arize]${NC} $*"; }
30
+ warn() { echo -e "${YELLOW}[arize]${NC} $*"; }
31
+ err() { echo -e "${RED}[arize]${NC} $*" >&2; }
32
+ header() { echo -e "\n${BOLD}${BLUE}$*${NC}\n"; }
33
+ command_exists() { command -v "$1" &>/dev/null; }
34
+
35
+ # TTY input for curl|bash scenarios
36
+ _tty_in=""
37
+ if [[ -t 0 ]]; then _tty_in="/dev/stdin"
38
+ elif (exec 3< /dev/tty) 2>/dev/null; then exec 3<&-; _tty_in="/dev/tty"; fi
39
+
40
+ # Run a command with stdin wired to the user's TTY when possible.
41
+ # Under `curl | bash`, our own stdin is the pipe — not a terminal — so any
42
+ # subprocess that calls input() (e.g. tracing/<harness>/install.py) would hit
43
+ # EOFError on the very first prompt. Redirecting from _tty_in lets Python read
44
+ # from the actual terminal. No-op in non-interactive environments without a TTY.
45
+ run_with_tty() {
46
+ if [[ -n "$_tty_in" ]]; then
47
+ "$@" < "$_tty_in"
48
+ else
49
+ "$@"
50
+ fi
51
+ }
52
+
53
+ # -- Python discovery --------------------------------------------------------
54
+ find_python() {
55
+ local candidates=(python3 python /usr/bin/python3 /usr/local/bin/python3 "$HOME/.local/bin/python3")
56
+ [[ -d "$HOME/.pyenv/shims" ]] && candidates+=("$HOME/.pyenv/shims/python3")
57
+ [[ -x "/opt/homebrew/bin/python3" ]] && candidates+=("/opt/homebrew/bin/python3")
58
+ local conda_base
59
+ conda_base=$(conda info --base 2>/dev/null) && [[ -n "$conda_base" ]] && candidates+=("${conda_base}/bin/python3")
60
+ for p in "${candidates[@]}"; do
61
+ local resolved
62
+ if [[ "$p" == /* ]]; then resolved="$p"
63
+ else resolved=$(command -v "$p" 2>/dev/null || true); fi
64
+ [[ -z "$resolved" || ! -f "$resolved" ]] && continue
65
+ "$resolved" -c "import sys; assert sys.version_info >= (3, 9)" 2>/dev/null && { echo "$resolved"; return 0; }
66
+ done
67
+ return 1
68
+ }
69
+
70
+ # -- Venv helpers ------------------------------------------------------------
71
+ venv_python() {
72
+ [[ -x "${VENV_DIR}/bin/python" ]] && { echo "${VENV_DIR}/bin/python"; return; }
73
+ [[ -x "${VENV_DIR}/Scripts/python.exe" ]] && { echo "${VENV_DIR}/Scripts/python.exe"; return; }
74
+ return 1
75
+ }
76
+ venv_pip() {
77
+ [[ -x "${VENV_DIR}/bin/pip" ]] && { echo "${VENV_DIR}/bin/pip"; return; }
78
+ [[ -x "${VENV_DIR}/Scripts/pip.exe" ]] && { echo "${VENV_DIR}/Scripts/pip.exe"; return; }
79
+ return 1
80
+ }
81
+
82
+ # -- Repository download ----------------------------------------------------
83
+ git_sync_harness_repo() {
84
+ local branch="$1"
85
+ [[ -d "${INSTALL_DIR}/.git" ]] || return 1
86
+ info "Syncing with origin/${branch}..."
87
+ git -C "$INSTALL_DIR" fetch --depth 1 origin "$branch" 2>/dev/null \
88
+ && git -C "$INSTALL_DIR" checkout -B "$branch" FETCH_HEAD 2>/dev/null && return 0
89
+ git -C "$INSTALL_DIR" fetch origin "$branch" 2>/dev/null \
90
+ && git -C "$INSTALL_DIR" checkout -B "$branch" FETCH_HEAD 2>/dev/null && return 0
91
+ warn "git fetch/checkout failed — trying pull --ff-only"
92
+ git -C "$INSTALL_DIR" pull --ff-only origin "$branch" 2>/dev/null && return 0
93
+ git -C "$INSTALL_DIR" pull --ff-only 2>/dev/null && return 0
94
+ return 1
95
+ }
96
+
97
+ install_repo_tarball() {
98
+ local tarball_url="${1:-$TARBALL_URL}"
99
+ info "Downloading coding-harness-tracing tarball..."
100
+ local tmp_tar; tmp_tar="$(mktemp)"
101
+ if command_exists curl; then curl -sSfL "$tarball_url" -o "$tmp_tar"
102
+ elif command_exists wget; then wget -qO "$tmp_tar" "$tarball_url"
103
+ else rm -f "$tmp_tar"; err "Neither curl nor wget found — cannot download"; exit 1; fi
104
+ mkdir -p "$INSTALL_DIR"
105
+ tar xzf "$tmp_tar" --strip-components=1 -C "$INSTALL_DIR"
106
+ rm -f "$tmp_tar"
107
+ info "Extracted to ${INSTALL_DIR}"
108
+ }
109
+
110
+ install_repo() {
111
+ # Wheel mode fetches nothing. The wheel carries every module the harness
112
+ # needs, so there is no source tree to place — but install.sh itself has to
113
+ # land in INSTALL_DIR, because `status`, `update` and `uninstall` are all
114
+ # documented as running from there and repo mode gets it via the extract.
115
+ if [[ -n "$WHEEL_DIR" ]]; then
116
+ mkdir -p "$INSTALL_DIR"
117
+ if [[ -f "${BASH_SOURCE[0]}" ]] && ! cmp -s "${BASH_SOURCE[0]}" "${INSTALL_DIR}/install.sh"; then
118
+ cp "${BASH_SOURCE[0]}" "${INSTALL_DIR}/install.sh" && chmod +x "${INSTALL_DIR}/install.sh"
119
+ fi
120
+ return 0
121
+ fi
122
+ git_sync_harness_repo "$INSTALL_BRANCH" && return 0
123
+ install_repo_tarball
124
+ }
125
+
126
+ # Invoke a harness's install.py. Repo mode runs the file from the source tree;
127
+ # wheel mode has no source tree, so it runs the same code as a module. Both
128
+ # resolve `core.*` from site-packages either way — the package is pip-installed,
129
+ # never on sys.path by accident — so these are equivalent, not a fallback.
130
+ run_harness_py() {
131
+ local key="$1" vp="$2"; shift 2
132
+ local dir; dir=$(harness_dir "$key") || return 1
133
+ if [[ -f "${INSTALL_DIR}/${dir}/install.py" ]]; then
134
+ run_with_tty "$vp" "${INSTALL_DIR}/${dir}/install.py" "$@"
135
+ else
136
+ run_with_tty "$vp" -m "${dir//\//.}.install" "$@"
137
+ fi
138
+ }
139
+
140
+ # -- Venv setup --------------------------------------------------------------
141
+
142
+ # Fix SSL certificate verification on macOS.
143
+ #
144
+ # Python.org installers ship their own OpenSSL that doesn't trust the macOS
145
+ # system keychain, so urllib (used by every arize-hook-*) fails with
146
+ # "CERTIFICATE_VERIFY_FAILED" against https://otlp.arize.com.
147
+ #
148
+ # Fix: install certifi into the venv and write a sitecustomize.py that sets
149
+ # SSL_CERT_FILE before any hook code runs. Idempotent — safe to call repeatedly.
150
+ _fix_macos_ssl_certs() {
151
+ local pip="$1"
152
+ local vp
153
+ vp=$(venv_python 2>/dev/null) || return 0
154
+
155
+ local offline=()
156
+ [[ -n "$WHEEL_DIR" ]] && offline=(--no-index --find-links "$WHEEL_DIR")
157
+ if ! "$pip" install --quiet "${offline[@]+"${offline[@]}"}" certifi 2>/dev/null; then
158
+ warn "Could not install certifi — SSL verification may fail on macOS"
159
+ [[ -n "$WHEEL_DIR" ]] && warn "Bundle a certifi wheel in ${WHEEL_DIR} to fix this offline."
160
+ return 0
161
+ fi
162
+
163
+ local certifi_where site_dir sc
164
+ certifi_where=$("$vp" -c "import certifi; print(certifi.where())" 2>/dev/null) || return 0
165
+ [[ -z "$certifi_where" ]] && return 0
166
+
167
+ site_dir=$("$vp" -c "import site; print(site.getsitepackages()[0])" 2>/dev/null) || return 0
168
+ sc="${site_dir}/sitecustomize.py"
169
+
170
+ cat > "$sc" <<'PYEOF'
171
+ # Arize Coding Harness Tracing: point Python's SSL stack at certifi's CA bundle on macOS.
172
+ # This runs automatically at interpreter startup, before any hook code.
173
+ import os as _os
174
+ try:
175
+ import certifi as _certifi
176
+ _bundle = _certifi.where()
177
+ _os.environ.setdefault("SSL_CERT_FILE", _bundle)
178
+ _os.environ.setdefault("REQUESTS_CA_BUNDLE", _bundle)
179
+ except ImportError:
180
+ pass
181
+ PYEOF
182
+ info "SSL certificates configured via certifi"
183
+ }
184
+
185
+ # Install the package into the venv. Extra args go to pip (`-U` for update).
186
+ # Shared so install and update cannot drift: they were the same wheel/repo branch
187
+ # twice, differing only by -U, and a flag added to one would have missed the other.
188
+ pip_install_harness() {
189
+ local pip="$1"; shift
190
+ if [[ -n "$WHEEL_DIR" ]]; then
191
+ # --no-index so a missing wheel fails loudly instead of quietly reaching
192
+ # PyPI, which would defeat the point of installing offline.
193
+ "$pip" install --quiet "$@" --no-index --find-links "$WHEEL_DIR" coding-harness-tracing \
194
+ || { err "Failed to install coding-harness-tracing from ${WHEEL_DIR}"; return 1; }
195
+ else
196
+ "$pip" install --quiet "$@" "$INSTALL_DIR" 2>/dev/null \
197
+ || { err "Failed to install coding-harness-tracing package"; return 1; }
198
+ fi
199
+ }
200
+
201
+ setup_venv() {
202
+ local python_cmd="$1"
203
+ if ! venv_python &>/dev/null; then
204
+ info "Creating venv..."
205
+ "$python_cmd" -m venv "$VENV_DIR" 2>/dev/null || {
206
+ err "Failed to create venv with $python_cmd"
207
+ err "You may need to install the venv module: apt install python3-venv (Debian/Ubuntu)"
208
+ return 1
209
+ }
210
+ fi
211
+ local pip; pip=$(venv_pip) || { err "pip not found in venv"; return 1; }
212
+ info "Installing coding-harness-tracing into venv..."
213
+ pip_install_harness "$pip" || return 1
214
+
215
+ [[ "$(uname)" == "Darwin" ]] && _fix_macos_ssl_certs "$pip"
216
+
217
+ info "Venv ready at ${VENV_DIR}"
218
+ }
219
+
220
+ # -- Harness name mapping ----------------------------------------------------
221
+ #
222
+ # Accepts both the CLI name and the config key. They are the same for every
223
+ # harness except Claude Code, which writes HARNESS_NAME "claude-code" while its
224
+ # CLI name is "claude". `update` and full `uninstall` discover harnesses via
225
+ # list_installed_harnesses(), which yields *config keys*, so without the alias
226
+ # both skipped Claude Code entirely — a full uninstall wiped the venv and left
227
+ # its hooks in ~/.claude/settings.json pointing at the deleted path.
228
+ # install.bat has accepted both spellings all along.
229
+ harness_dir() {
230
+ case "$1" in
231
+ claude|claude-code) echo "tracing/claude_code" ;;
232
+ codex) echo "tracing/codex" ;;
233
+ copilot) echo "tracing/copilot" ;;
234
+ cursor) echo "tracing/cursor" ;;
235
+ gemini) echo "tracing/gemini" ;;
236
+ kiro) echo "tracing/kiro" ;;
237
+ antigravity) echo "tracing/antigravity" ;;
238
+ opencode) echo "tracing/opencode" ;;
239
+ omp) echo "tracing/omp" ;;
240
+ devin) echo "tracing/devin" ;;
241
+ *) return 1 ;;
242
+ esac
243
+ }
244
+
245
+ install_harness() {
246
+ local cmd="$1" skills="$2"
247
+ harness_dir "$cmd" >/dev/null || { err "Unknown harness: ${cmd}"; usage; exit 1; }
248
+ header "Installing ${cmd} tracing"
249
+ local python_cmd; python_cmd=$(find_python) || { err "No Python 3.9+ found"; exit 1; }
250
+ info "Found Python: ${python_cmd} ($("$python_cmd" --version 2>&1))"
251
+ install_repo
252
+ setup_venv "$python_cmd"
253
+ local vp; vp=$(venv_python) || { err "Venv python not found after setup"; exit 1; }
254
+ info "Migrating legacy config.yaml to config.json (if present)..."
255
+ "$vp" -m core.config migrate || true
256
+ if [[ "$skills" == true ]]; then
257
+ run_harness_py "$cmd" "$vp" install --with-skills
258
+ else
259
+ run_harness_py "$cmd" "$vp" install
260
+ fi
261
+ info "Setup complete!"
262
+ }
263
+
264
+ usage() {
265
+ cat <<'EOF'
266
+
267
+ Arize Coding Harness Tracing Installer
268
+
269
+ Usage: install.sh <command> [flags]
270
+
271
+ Commands:
272
+ claude Install and configure tracing for Claude Code / Agent SDK
273
+ codex Install and configure tracing for OpenAI Codex CLI
274
+ copilot Install and configure tracing for GitHub Copilot (VS Code + CLI)
275
+ cursor Install and configure tracing for Cursor IDE
276
+ gemini Install and configure tracing for Gemini CLI
277
+ kiro Install and configure tracing for Kiro CLI
278
+ antigravity Install and configure tracing for Google Antigravity CLI/IDE
279
+ opencode Install and configure tracing for opencode
280
+ omp Install and configure tracing for Oh My Pi (omp)
281
+ devin Install and configure tracing for Devin CLI
282
+ status Report configured harnesses and whether their hooks are wired up
283
+ update Update the installed coding-harness-tracing and re-register all harnesses
284
+ uninstall <harness> Tear down one harness
285
+ uninstall Full wipe: venv + repo + shared config
286
+
287
+ Flags:
288
+ --with-skills Symlink harness skills into .agents/skills/
289
+ --branch NAME Install from a specific git branch (default: main)
290
+ --wheel-dir DIR Install from local wheels in DIR instead of downloading
291
+ the repo. No network and no remote code execution; also
292
+ settable as ARIZE_WHEEL_DIR. Bundle a certifi wheel
293
+ alongside it to keep macOS SSL working offline.
294
+ --json With `status`: emit machine-readable JSON. Exit code is
295
+ 0 all wired up, 1 nothing configured, 2 hooks missing.
296
+ --non-interactive, -y Ask nothing; read every value from the environment or a
297
+ file named by ARIZE_ENV_FILE. Missing required
298
+ values are an error.
299
+
300
+ Non-interactive install:
301
+ Values come from the environment, or from a dotenv file named with
302
+ ARIZE_ENV_FILE — which keeps the API key out of the command line and shell
303
+ history. A named file outranks the environment, so there is no automatic
304
+ ./.env search: a cloned repo's dotenv must not get to choose the endpoint
305
+ your credentials are sent to.
306
+
307
+ ARIZE_API_KEY, ARIZE_SPACE_ID Arize AX credentials (both required)
308
+ PHOENIX_ENDPOINT, PHOENIX_API_KEY Phoenix credentials
309
+ ARIZE_BACKEND arize|phoenix (default: inferred — a space
310
+ ID means Arize AX, a Phoenix endpoint
311
+ means Phoenix)
312
+ ARIZE_PROJECT_NAME Project name (default: the harness name)
313
+ ARIZE_USER_ID Optional user ID stamped on spans
314
+ ARIZE_OTLP_ENDPOINT Override otlp.arize.com:443
315
+ ARIZE_LOG_PROMPTS Set true to capture prompt text (off here)
316
+ ARIZE_LOG_TOOL_DETAILS Set true to capture tool commands and paths
317
+ ARIZE_LOG_TOOL_CONTENT Set true to capture tool output
318
+
319
+ Example — credentials straight from a dotenv file, nothing exported:
320
+ ax api-keys create --env-file ~/.arize/onboarding.env
321
+ echo 'ARIZE_SPACE_ID=<space-id>' >> ~/.arize/onboarding.env
322
+ ARIZE_ENV_FILE=~/.arize/onboarding.env ./install.sh claude --non-interactive
323
+
324
+ EOF
325
+ }
326
+
327
+ # -- Main dispatch -----------------------------------------------------------
328
+ main() {
329
+ local cmd="${1:-}"; shift || true
330
+ local subcmd="" with_skills=false status_args=""
331
+ local args=("$@") i=0
332
+ while [[ $i -lt ${#args[@]} ]]; do
333
+ case "${args[$i]}" in
334
+ --with-skills) with_skills=true ;;
335
+ --non-interactive|-y) export ARIZE_NONINTERACTIVE=1 ;;
336
+ --json) status_args="--json" ;;
337
+ --branch)
338
+ i=$((i + 1))
339
+ INSTALL_BRANCH="${args[$i]:-main}"
340
+ TARBALL_URL="https://github.com/Arize-ai/coding-harness-tracing/archive/refs/heads/${INSTALL_BRANCH}.tar.gz"
341
+ ;;
342
+ --wheel-dir)
343
+ i=$((i + 1))
344
+ WHEEL_DIR="${args[$i]:-}"
345
+ [[ -d "$WHEEL_DIR" ]] || { err "--wheel-dir needs a directory; got '${WHEEL_DIR}'"; exit 1; }
346
+ WHEEL_DIR="$(cd "$WHEEL_DIR" && pwd)"
347
+ compgen -G "${WHEEL_DIR}/coding_harness_tracing-*.whl" >/dev/null \
348
+ || { err "No coding_harness_tracing-*.whl in ${WHEEL_DIR}"; exit 1; }
349
+ ;;
350
+ *) [[ -z "$subcmd" ]] && subcmd="${args[$i]}" ;;
351
+ esac
352
+ i=$((i + 1))
353
+ done
354
+
355
+ case "$cmd" in
356
+ claude|codex|copilot|cursor|gemini|kiro|antigravity|opencode|omp|devin)
357
+ install_harness "$cmd" "$with_skills"
358
+ ;;
359
+ uninstall)
360
+ if [[ -n "$subcmd" ]]; then
361
+ harness_dir "$subcmd" >/dev/null || { err "Unknown harness: ${subcmd}"; usage; exit 1; }
362
+ local vp; vp=$(venv_python) || { err "Venv not found — nothing to uninstall"; exit 1; }
363
+ header "Uninstalling ${subcmd} tracing"
364
+ run_harness_py "$subcmd" "$vp" uninstall
365
+ else
366
+ local vp; vp=$(venv_python) || {
367
+ warn "Venv not found — removing install directory"; rm -rf "$INSTALL_DIR"
368
+ info "Uninstall complete."; return 0; }
369
+ header "Full uninstall"
370
+ # Run each installed harness's uninstall first so external
371
+ # registrations (settings.json hooks, config.toml notify,
372
+ # cursor hooks.json, .github/hooks/*) are cleaned before the
373
+ # shared runtime is wiped. wipe.py deliberately does not
374
+ # touch those files.
375
+ local harnesses
376
+ harnesses=$("$vp" -c 'from core.setup import list_installed_harnesses as L; print("\n".join(L()))' 2>/dev/null) || true
377
+ if [[ -n "$harnesses" ]]; then
378
+ while IFS= read -r key; do
379
+ harness_dir "$key" >/dev/null || { warn "Unknown harness: ${key} (skipping)"; continue; }
380
+ info "Uninstalling ${key} tracing..."
381
+ run_harness_py "$key" "$vp" uninstall || warn "${key} uninstall failed (continuing)"
382
+ done <<< "$harnesses"
383
+ fi
384
+ "$vp" -m core.setup.wipe
385
+ fi
386
+ ;;
387
+ status)
388
+ local vp; vp=$(venv_python) || { err "Venv not found — nothing installed"; exit 1; }
389
+ "$vp" -m core.setup.status $status_args
390
+ ;;
391
+ update)
392
+ header "Updating coding-harness-tracing"
393
+ # Re-registering runs each harness's installer, which prompts for the
394
+ # project name. With no terminal to answer on that used to die with an
395
+ # EOFError, so fall back to stored values there — and only there, so an
396
+ # interactive update keeps every prompt it has today.
397
+ [[ -n "$_tty_in" ]] || export ARIZE_NONINTERACTIVE=1
398
+ # A wheel install has no repo to pull and no newer wheel to hand us.
399
+ # Silently converting it to a network install would change how it was
400
+ # installed behind the user's back, so refuse and say who can update.
401
+ if [[ -z "$WHEEL_DIR" && ! -d "${INSTALL_DIR}/.git" && ! -f "${INSTALL_DIR}/pyproject.toml" ]]; then
402
+ err "This looks like an offline install with no source tree to update."
403
+ err "Re-run the installer that created it (for npx evals, update that), or"
404
+ err "pass --wheel-dir <dir> with a newer wheel."
405
+ exit 1
406
+ fi
407
+ if [[ -n "$WHEEL_DIR" ]]; then
408
+ info "Updating from local wheels in ${WHEEL_DIR}..."
409
+ elif [[ -d "${INSTALL_DIR}/.git" ]]; then
410
+ info "Pulling latest changes..."
411
+ git -C "$INSTALL_DIR" pull --ff-only 2>/dev/null || {
412
+ warn "git pull failed — falling back to tarball re-extract"; install_repo_tarball; }
413
+ else install_repo_tarball; fi
414
+ local pip; pip=$(venv_pip) || { err "Venv not found — run install first"; exit 1; }
415
+ info "Reinstalling coding-harness-tracing..."
416
+ pip_install_harness "$pip" -U || exit 1
417
+ local vp; vp=$(venv_python) || { err "venv python not found"; exit 1; }
418
+ info "Migrating legacy config.yaml to config.json (if present)..."
419
+ "$vp" -m core.config migrate || true
420
+ local harnesses
421
+ harnesses=$("$vp" -c 'from core.setup import list_installed_harnesses as L; print("\n".join(L()))' 2>/dev/null) || true
422
+ if [[ -n "$harnesses" ]]; then
423
+ while IFS= read -r key; do
424
+ harness_dir "$key" >/dev/null || { warn "Unknown harness: ${key} (skipping)"; continue; }
425
+ # Keep going, as the uninstall loop does: one harness whose
426
+ # registration fails should not abandon the rest half-updated.
427
+ info "Re-registering ${key}..."
428
+ run_harness_py "$key" "$vp" install || warn "${key} re-registration failed (continuing)"
429
+ done <<< "$harnesses"
430
+ else info "No installed harnesses found to re-register"; fi
431
+ info "Update complete."
432
+ ;;
433
+ -h|--help|help) usage ;;
434
+ "") usage; exit 1 ;;
435
+ *) err "Unknown command: ${cmd}"; usage; exit 1 ;;
436
+ esac
437
+ }
438
+
439
+ main "$@"
@@ -0,0 +1,18 @@
1
+ {
2
+ "repo": "https://github.com/Arize-ai/coding-harness-tracing",
3
+ "ref": "main",
4
+ "resolvedSha": "dd3ed1c455daab380e915fc5404a167811c460f0",
5
+ "source": "git",
6
+ "wheel": "coding_harness_tracing-0.1.0-py3-none-any.whl",
7
+ "wheelVersion": "0.1.0",
8
+ "wheelSha256": "e17d86afa36365ddd451325b7b2ed3e35ba85801fb10e35401fded609095f550",
9
+ "files": [
10
+ "LICENSE-coding-harness-tracing",
11
+ "MANIFEST",
12
+ "certifi-2026.7.22-py3-none-any.whl",
13
+ "coding_harness_tracing-0.1.0-py3-none-any.whl",
14
+ "harness-install.bat",
15
+ "harness-install.sh",
16
+ "python_dotenv-1.2.1-py3-none-any.whl"
17
+ ]
18
+ }