@osfactory/har 0.5.0 → 0.6.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.
package/dist/index.js CHANGED
@@ -26921,6 +26921,7 @@ var REQUIRED_FILES_DEFAULT = [
26921
26921
  "harness.env",
26922
26922
  "setup-infra.sh",
26923
26923
  "launch.sh",
26924
+ "provision-toolchain.sh",
26924
26925
  "verify.sh",
26925
26926
  "teardown.sh",
26926
26927
  "agent-cli.sh",
@@ -26939,6 +26940,7 @@ var REQUIRED_FILES_IOS = [
26939
26940
  "harness.env",
26940
26941
  "setup-infra.sh",
26941
26942
  "launch.sh",
26943
+ "provision-toolchain.sh",
26942
26944
  "verify.sh",
26943
26945
  "teardown.sh",
26944
26946
  "agent-cli.sh",
@@ -26955,6 +26957,7 @@ function getRequiredFiles(repoPath) {
26955
26957
  var SHELL_SCRIPTS = [
26956
26958
  "setup-infra.sh",
26957
26959
  "launch.sh",
26960
+ "provision-toolchain.sh",
26958
26961
  "verify.sh",
26959
26962
  "teardown.sh",
26960
26963
  "agent-cli.sh",
@@ -27466,9 +27469,9 @@ var fs17 = __toESM(require("fs"));
27466
27469
  var path15 = __toESM(require("path"));
27467
27470
  var ADAPTATION_PROMPT_FILE = "ADAPT-PROMPT.md";
27468
27471
  var PROFILE_HINTS = {
27469
- default: "Web app profile \u2014 Docker Compose for shared infra (HARNESS_INFRA_SERVICES), PM2 for the primary application only, git worktree per agent slot by default. Identify the primary app agents modify; run supporting services shared. Adapt docker-compose, setup-infra, and ecosystem templates for this stack.",
27470
- cli: "CLI/library profile \u2014 no PM2. Optional Docker Compose via the `HARNESS_INFRA_SERVICES` list in harness.env. Agents work in an isolated git worktree by default (`--no-worktree` to use the repo root). Remove any leftover PM2/ecosystem files; keep docker-compose when shared services are enabled.",
27471
- ios: "iOS mobile app profile \u2014 no PM2, no web ports. Agents build and test via xcodebuild in an isolated git worktree. Set HARNESS_XCODE_SCHEME, HARNESS_XCODE_WORKSPACE/PROJECT, HARNESS_SIMULATOR_NAME, and HARNESS_BUNDLE_ID in harness.env. Adapt setup-infra.sh to boot the target simulator. Install the RocketSim stage template (har env add-stage rocketsim) for user-flow validation."
27472
+ default: "Web app profile (SaaS/full-stack) \u2014 Docker Compose for shared infra (HARNESS_INFRA_SERVICES), PM2 for the primary application only, git worktree per agent slot by default. Launch provisions toolchain via harness.env (HARNESS_ECOSYSTEM, HARNESS_INSTALL_CMD) and writes paths to .env.agent.<id>. Identify the primary app agents modify; run supporting services shared.",
27473
+ cli: "CLI/library profile (typical SWE-bench) \u2014 no PM2. Optional Docker Compose via HARNESS_INFRA_SERVICES. Git worktree by default. Launch provisions toolchain declaratively (HARNESS_ECOSYSTEM auto-detects common ecosystems); verify must use resolved tool paths from .env.agent.<id>, never hardcoded interpreter or package-manager paths.",
27474
+ ios: "iOS mobile app profile \u2014 xcodebuild + iOS Simulator in an isolated git worktree. Set HARNESS_XCODE_SCHEME, workspace/project, HARNESS_SIMULATOR_NAME, HARNESS_BUNDLE_ID. Launch writes XCODEBUILD_BIN to .env.agent.<id>; verify uses it. Install RocketSim stage (har env add-stage rocketsim) for user-flow validation."
27472
27475
  };
27473
27476
  function loadTemplate(name) {
27474
27477
  const filePath = resolveTemplateFile(name);
@@ -6,6 +6,24 @@ Explore this repository, then edit files in `.har/` directly to make the harness
6
6
 
7
7
  **Do NOT** create a YAML config or JSON mapping file for runtime behavior. Put behavior directly in the harness scripts and templates.
8
8
 
9
+ ## HAR profiles (pick the right one at init)
10
+
11
+ `har env init` scaffolds from one of three boilerplate profiles — **choose the profile that matches this repository**:
12
+
13
+ | Profile | Best for | What you get |
14
+ |---------|----------|--------------|
15
+ | `default` | **SaaS / web apps** (Next.js, Rails, full-stack, etc.) | Docker Compose shared infra, PM2 for primary app, per-slot ports and preview URLs |
16
+ | `cli` | **CLI tools, libraries, test-suite repos** (typical SWE-bench) | Git worktree by default, no PM2; optional Docker for databases; run project commands in isolation |
17
+ | `ios` | **Mobile iOS / Swift** | xcodebuild + iOS Simulator; scheme/project/simulator in `harness.env` |
18
+
19
+ ```bash
20
+ har env init # default (SaaS/web)
21
+ har env init --profile cli # libraries / CLI / polyglot test repos
22
+ har env init --profile ios # iOS apps
23
+ ```
24
+
25
+ Do **not** disable worktrees, rewrite launch into repo-root-only mode, or pick the wrong profile unless the project truly requires it.
26
+
9
27
  ## Profile: {{PROFILE}}
10
28
 
11
29
  {{PROFILE_HINT}}
@@ -38,13 +56,18 @@ Clear index of the harness: what each file does, quick start, architecture, how
38
56
  ### `.har/harness.env`
39
57
  Primary app, ports, `HARNESS_INFRA_SERVICES`, migrate/seed commands, health check path.
40
58
 
59
+ **Toolchain provisioning:** set `HARNESS_ECOSYSTEM` (`auto` detects from manifests) and optional `HARNESS_INSTALL_CMD`. Launch runs `provision-toolchain.sh`, writes resolved paths (`PYTHON_BIN`, `NODE_BIN`, `NPM_BIN`, `XCODEBUILD_BIN`, …) into `.env.agent.<id>`. Verify steps **must** use those paths — never hardcode venv or interpreter locations.
60
+
41
61
  ### `.har/ecosystem.agent.template.cjs` (default profile only)
42
62
  PM2 processes for the primary application only, matching how it runs in dev. Skip entirely for the CLI profile.
43
63
 
44
64
  ### `.har/verify.sh`
45
65
  Adapt verification for this repository's toolchain. Step lists in the template are
46
66
  **examples, not exhaustive** — add, remove, or reorder `run_step` calls to match
47
- how this project is built and tested.
67
+ how this project is built and tested. Use toolchain variables from `.env.agent.<id>`
68
+ (e.g. `${NPM_BIN:-npm}`, `${PYTHON_BIN:-python3}`, `${XCODEBUILD_BIN:-xcodebuild}`).
69
+ The stock verify section is keyed by `HARNESS_ECOSYSTEM`; it is a starting point,
70
+ not the repo's final contract. Replace conventions that do not match this project.
48
71
 
49
72
  **Tier contract:**
50
73
 
@@ -56,6 +79,7 @@ how this project is built and tested.
56
79
  - **Quick** must stay fast and minimal (syntax, compile, import smoke) — not the full test suite.
57
80
  - **Full** holds unit tests, lint, and heavier checks; optional Playwright runs on `--full` when installed.
58
81
  - Reuse real commands from `package.json`, `Makefile`, CI, `pyproject.toml`, etc.
82
+ - Remove stock npm/pytest/go/cargo/maven/gradle examples that do not apply.
59
83
  - Replace all TODO placeholders in both tiers.
60
84
 
61
85
  ### Readiness vs liveness (required)
@@ -88,8 +112,9 @@ scripts or document why no substitute is needed. In particular:
88
112
  choose and document an agent-friendly asset mode.
89
113
  - Put Layer 3 checks in `verify --full`, a project-owned readiness script, or
90
114
  documented smoke URLs. Health alone is not sufficient for UI/auth apps.
91
- - Ensure `launch.sh` writes the slot registry before slow or fragile steps, and
92
- `verify.sh` resolves env/work dir through `agent-slot.sh`.
115
+ - Ensure `launch.sh` writes the slot registry before slow or fragile steps, runs
116
+ `provision-toolchain.sh` to install deps and record toolchain paths in
117
+ `.env.agent.<id>`, and `verify.sh` resolves env/work dir through `agent-slot.sh`.
93
118
 
94
119
  ### Optional Playwright stage
95
120
  If the user ran `har env add-stage playwright` (or `@playwright/test` is in package.json):
@@ -22,8 +22,8 @@ Prefer targeted edits over full rewrites. Key files to review:
22
22
  ### `.har/README.md` (required)
23
23
  Keep this accurate — it is the harness index. Update whenever scripts, stages, or workflow change.
24
24
 
25
- ### `.har/harness.env`, `verify.sh`, `ecosystem.agent.template.cjs`, `CLAUDE.agent.md`
26
- Align commands and instructions with the current stack.
25
+ ### `.har/harness.env`, `verify.sh`, `provision-toolchain.sh`, `ecosystem.agent.template.cjs`, `CLAUDE.agent.md`
26
+ Align commands and instructions with the current stack. Verify steps must use toolchain paths from `.env.agent.<id>` (`PYTHON_BIN`, `NPM_BIN`, `XCODEBUILD_BIN`, …) — never hardcoded venv or interpreter paths. Replace stock ecosystem conventions that do not match the repository; do not leave npm/pytest/go/cargo/maven/gradle examples in place by accident.
27
27
 
28
28
  ### `.har/env.template`, `setup-infra.sh`, `docker-compose.agent.yml`
29
29
  Update only if infra changed.
@@ -50,10 +50,12 @@ Quick loop during development: MCP `har_run_verification`, `har env verify ${AGE
50
50
  ## Project commands
51
51
 
52
52
  ```bash
53
- # TODO: adapt for this repository (see package.json, Makefile, etc.)
53
+ # TODO: adapt for this repository (see package.json, Makefile, pyproject.toml, CI, etc.)
54
54
  # npm run typecheck
55
- # npm test
56
- # npm run lint
55
+ # pytest -q
56
+ # go test ./...
57
+ # cargo test
58
+ # make test
57
59
  ```
58
60
 
59
61
  ## Do not
@@ -12,14 +12,15 @@ Generated and maintained by [`har`](https://github.com/antoineFrau/har). Run `ha
12
12
  |------|---------|
13
13
  | `README.md` | This file — index of the harness |
14
14
  | `manifest.json` | Generator metadata (version, checksums) — do not edit |
15
- | `harness.env` | Shared config: primary app, ports, agent slot limits, `HARNESS_INFRA_SERVICES`, migrate/seed commands |
15
+ | `harness.env` | Shared config: primary app, ports, agent slot limits, `HARNESS_INFRA_SERVICES`, toolchain provisioning, migrate/seed commands |
16
16
  | `stages.json` | Machine-readable registry of runnable harness stages |
17
17
  | `stages/` | Optional custom stage scripts registered from `stages.json` |
18
18
  | `runs/` | Run history from `har env` / MCP only — `.har/runs/YYYY-MM-DD/HH-mm-ss_<stageId>_agent-<id>.json` (gitignore) |
19
19
  | `artifacts/` | Stage outputs: reports, traces, screenshots, logs |
20
20
  | `agent-slot.sh` | Shared agent-id validation (reads limits from `harness.env`) |
21
21
  | `setup-infra.sh` | Start shared Docker infra + create template database |
22
- | `launch.sh` | Launch one agent slot (ports, DB clone, PM2 processes) |
22
+ | `launch.sh` | Launch one agent slot (ports, DB clone, toolchain provisioning, PM2 processes) |
23
+ | `provision-toolchain.sh` | Install deps and write toolchain paths to `.env.agent.<id>` |
23
24
  | `verify.sh` | Verification pipeline (smoke by default; --full adds tests, lint, e2e) |
24
25
  | `teardown.sh` | Tear down one agent slot |
25
26
  | `agent-cli.sh` | Manage a running agent (status, logs, psql, health) |
@@ -49,8 +50,8 @@ In Cursor with HAR MCP configured: use `har_launch_environment`, `har_run_verifi
49
50
  ```bash
50
51
  ./.har/setup-infra.sh # when HARNESS_INFRA_SERVICES is non-empty
51
52
  ./.har/launch.sh 1
52
- ./.har/verify.sh 1 # quick: smoke (typecheck + health)
53
- ./.har/verify.sh 1 --full # + unit tests, lint, browser-e2e (if Playwright stage installed)
53
+ ./.har/verify.sh 1 # quick: ecosystem smoke + health
54
+ ./.har/verify.sh 1 --full # + conventional tests/lint, browser-e2e (if installed)
54
55
  ./.har/teardown.sh 1
55
56
  ```
56
57
 
@@ -64,8 +65,13 @@ not a fixed command list.
64
65
 
65
66
  | Mode | Command | Typical steps |
66
67
  |------|---------|---------------|
67
- | Quick | `har env verify <id>` or `verify.sh <id>` | Smoke: compile / typecheck / health (stops early on failure) |
68
- | Full | `har env verify <id> --full` or `verify.sh <id> --full` | + unit tests, lint, optional readiness smoke + **`browser-e2e`** when `.har/stages/browser-e2e.sh` exists |
68
+ | Quick | `har env verify <id>` or `verify.sh <id>` | Stock ecosystem smoke + health (stops early on failure) |
69
+ | Full | `har env verify <id> --full` or `verify.sh <id> --full` | Stock conventional tests/lint, optional readiness smoke + **`browser-e2e`** when `.har/stages/browser-e2e.sh` exists |
70
+
71
+ The stock commands are deliberately generic conventions keyed by
72
+ `HARNESS_ECOSYSTEM`. Replace them with the repository's real commands during
73
+ adaptation; do not leave Node/npm, Python, Go, Rust, Java, or Ruby defaults in
74
+ place when they do not match the project.
69
75
 
70
76
  Install Playwright stage: `har env add-stage playwright` (optional). UI changes should add or update specs under `tests/`.
71
77
 
@@ -23,6 +23,8 @@ module.exports = {
23
23
  // .har/ecosystem.shared.config.cjs (started by setup-infra.sh, processes
24
24
  // named "har-shared-<name>", fixed ports). A primary app may still need two
25
25
  // processes (e.g. api + frontend) — add one entry per process.
26
+ // Example Node dev process. Replace with this repo's actual primary app
27
+ // command (Rails, Django, Go, Java, etc. should not keep this npm entry).
26
28
  {
27
29
  name: 'agent-${AGENT_ID}-api',
28
30
  script: 'npm',
@@ -32,6 +32,9 @@ S3_FORCE_PATH_STYLE=true
32
32
  # Headless browser (if enabled)
33
33
  HEADLESS_BROWSER_URL=http://localhost:${BROWSER_PORT}
34
34
 
35
+ # Toolchain paths — appended by provision-toolchain.sh during launch
36
+ # NODE_BIN= NPM_BIN= PYTHON_BIN= VIRTUAL_ENV= GO_BIN= CARGO_BIN= JAVA_HOME=
37
+
35
38
  # Mail (if enabled)
36
39
  SMTP_HOST=localhost
37
40
  SMTP_PORT=11025
@@ -19,6 +19,11 @@ export HARNESS_HEALTH_CHECK_PATH=/health
19
19
  export HARNESS_AGENT_SLOT_MIN=1
20
20
  export HARNESS_AGENT_SLOT_MAX=5
21
21
 
22
+ # Toolchain provisioning — launch writes resolved paths into .env.agent.<id>
23
+ # Ecosystem: auto (detect from manifests) | node | python | go | rust | java | ruby | none
24
+ export HARNESS_ECOSYSTEM="auto"
25
+ # export HARNESS_INSTALL_CMD="make deps"
26
+
22
27
  # Shared infrastructure — space-separated docker compose service names from
23
28
  # docker-compose.agent.yml. Started ONCE by setup-infra.sh and shared by all
24
29
  # agent slots on fixed ports. Prune/add services in the compose file, then
@@ -110,26 +110,12 @@ else
110
110
  log "Using repo root (worktree disabled)"
111
111
  fi
112
112
 
113
- # Install dependencies (fresh worktrees have no node_modules — PM2 config needs them at load time)
114
- if [ -f "$WORK_DIR/package.json" ] && [ ! -d "$WORK_DIR/node_modules" ]; then
115
- log "Installing dependencies in $WORK_DIR..."
116
- (cd "$WORK_DIR" && npm install --silent)
117
- elif [ -f "$WORK_DIR/go.mod" ] && [ ! -d "$WORK_DIR/vendor" ]; then
118
- log "Go module detected in work dir (run go mod download if needed)"
119
- fi
120
-
121
- # Monorepo: file:/workspace deps may resolve modules from the repo root — install there too
122
- if [ -n "${REL_PREFIX:-}" ] && [ -f "$WORKTREE_DIR/package.json" ] && [ ! -d "$WORKTREE_DIR/node_modules" ]; then
123
- log "Installing monorepo root dependencies in $WORKTREE_DIR..."
124
- (cd "$WORKTREE_DIR" && npm install --silent)
125
- fi
126
-
127
113
  # Generate .env.agent.N
128
114
  # Keep harness-generated files out of accidental `git add -A` commits in repos
129
115
  # whose .gitignore doesn't know about har (applies to all worktrees too).
130
116
  GIT_EXCLUDE="$(git -C "$REPO_ROOT" rev-parse --git-common-dir 2>/dev/null)/info/exclude"
131
117
  if [ -n "$GIT_EXCLUDE" ] && [ -d "$(dirname "$GIT_EXCLUDE")" ]; then
132
- for pattern in '.env.agent.*' 'ecosystem.agent.*.config.cjs'; do
118
+ for pattern in '.env.agent.*' 'ecosystem.agent.*.config.cjs' '.har/venv'; do
133
119
  grep -qxF "$pattern" "$GIT_EXCLUDE" 2>/dev/null || echo "$pattern" >> "$GIT_EXCLUDE"
134
120
  done
135
121
  fi
@@ -191,6 +177,14 @@ SLOT_STATUS="starting" \
191
177
  write_slot_registry
192
178
  REGISTRY_WRITTEN=true
193
179
 
180
+ log "Provisioning toolchain (see harness.env: HARNESS_ECOSYSTEM, HARNESS_INSTALL_CMD)..."
181
+ HAR_WORK_DIR="$WORK_DIR" \
182
+ HAR_ENV_FILE="$ENV_FILE" \
183
+ HAR_WORKTREE_DIR="${WORKTREE_DIR:-}" \
184
+ HAR_REL_PREFIX="${REL_PREFIX:-}" \
185
+ HAR_AGENT_ID="$AGENT_ID" \
186
+ "$SCRIPT_DIR/provision-toolchain.sh"
187
+
194
188
  if [ -n "${HARNESS_DB_MINIMAL_BOOTSTRAP_CMD:-}" ]; then
195
189
  log "Running minimal data bootstrap..."
196
190
  (cd "$WORK_DIR" && set -a && . "$ENV_FILE" && set +a && eval "$HARNESS_DB_MINIMAL_BOOTSTRAP_CMD")
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env bash
2
+ # Provision the project toolchain and append resolved paths to .env.agent.<id>.
3
+ # Called from launch.sh after the agent env file is created.
4
+ #
5
+ # Configure in harness.env:
6
+ # HARNESS_ECOSYSTEM — auto (default) | node | python | go | rust | java | ruby | ios | none
7
+ # HARNESS_INSTALL_CMD — optional override (eval in HAR_WORK_DIR)
8
+ # Ecosystem-specific optional overrides:
9
+ # HARNESS_PYTHON_VENV_DIR — Python venv path relative to HAR_WORK_DIR (default: .har/venv)
10
+ #
11
+ # Required env from caller: HAR_WORK_DIR, HAR_ENV_FILE
12
+ # Optional: HAR_WORKTREE_DIR, HAR_REL_PREFIX, HAR_AGENT_ID (for logging)
13
+ set -euo pipefail
14
+
15
+ : "${HAR_WORK_DIR:?HAR_WORK_DIR is required}"
16
+ : "${HAR_ENV_FILE:?HAR_ENV_FILE is required}"
17
+
18
+ HAR_WORKTREE_DIR="${HAR_WORKTREE_DIR:-}"
19
+ HAR_REL_PREFIX="${HAR_REL_PREFIX:-}"
20
+ HAR_AGENT_ID="${HAR_AGENT_ID:-}"
21
+
22
+ pt_log() {
23
+ if [ -n "$HAR_AGENT_ID" ]; then
24
+ echo "==> [agent-${HAR_AGENT_ID}] toolchain: $*" >&2
25
+ else
26
+ echo "==> [provision-toolchain] $*" >&2
27
+ fi
28
+ }
29
+
30
+ append_env() {
31
+ local key="$1"
32
+ local value="$2"
33
+ printf '%s=%s\n' "$key" "$value" >> "$HAR_ENV_FILE"
34
+ }
35
+
36
+ append_path_prefix() {
37
+ local prefix="$1"
38
+ [ -n "$prefix" ] && [ -d "$prefix" ] || return 0
39
+ append_env "PATH" "${prefix}:${PATH:-$PATH}"
40
+ }
41
+
42
+ detect_ecosystem() {
43
+ local dir="$1"
44
+ local configured="${HARNESS_ECOSYSTEM:-auto}"
45
+ if [ -n "$configured" ] && [ "$configured" != "auto" ]; then
46
+ echo "$configured"
47
+ return
48
+ fi
49
+ if [ -f "$dir/package.json" ]; then echo node; return; fi
50
+ if [ -f "$dir/pyproject.toml" ] || [ -f "$dir/setup.py" ] || [ -f "$dir/setup.cfg" ] \
51
+ || [ -f "$dir/requirements.txt" ] || [ -f "$dir/Pipfile" ]; then
52
+ echo python
53
+ return
54
+ fi
55
+ if [ -f "$dir/go.mod" ]; then echo go; return; fi
56
+ if [ -f "$dir/Cargo.toml" ]; then echo rust; return; fi
57
+ if [ -f "$dir/pom.xml" ] || [ -f "$dir/build.gradle" ] || [ -f "$dir/build.gradle.kts" ]; then
58
+ echo java
59
+ return
60
+ fi
61
+ if [ -f "$dir/Gemfile" ]; then echo ruby; return; fi
62
+ if [ -n "${HARNESS_XCODE_SCHEME:-}" ] || [ -n "${HARNESS_XCODE_PROJECT:-}" ] \
63
+ || [ -n "${HARNESS_XCODE_WORKSPACE:-}" ]; then
64
+ echo ios
65
+ return
66
+ fi
67
+ echo none
68
+ }
69
+
70
+ run_install_cmd() {
71
+ local dir="$1"
72
+ if [ -n "${HARNESS_INSTALL_CMD:-}" ]; then
73
+ pt_log "Running HARNESS_INSTALL_CMD..."
74
+ (cd "$dir" && eval "$HARNESS_INSTALL_CMD")
75
+ return
76
+ fi
77
+ return 1
78
+ }
79
+
80
+ provision_node() {
81
+ local dir="$1"
82
+ local npm_bin="npm"
83
+ local node_bin="node"
84
+
85
+ if ! run_install_cmd "$dir"; then
86
+ pt_log "Installing Node dependencies..."
87
+ if [ -f "$dir/pnpm-lock.yaml" ] && command -v pnpm >/dev/null 2>&1; then
88
+ (cd "$dir" && pnpm install --silent)
89
+ npm_bin="pnpm"
90
+ elif [ -f "$dir/yarn.lock" ] && command -v yarn >/dev/null 2>&1; then
91
+ (cd "$dir" && yarn install --silent)
92
+ npm_bin="yarn"
93
+ else
94
+ (cd "$dir" && npm install --silent)
95
+ fi
96
+ fi
97
+
98
+ if command -v node >/dev/null 2>&1; then
99
+ node_bin="$(command -v node)"
100
+ fi
101
+ if [ "$npm_bin" = "npm" ] && command -v npm >/dev/null 2>&1; then
102
+ npm_bin="$(command -v npm)"
103
+ fi
104
+
105
+ append_env "HARNESS_ECOSYSTEM" "node"
106
+ append_env "NODE_BIN" "$node_bin"
107
+ append_env "NPM_BIN" "$npm_bin"
108
+ append_path_prefix "$dir/node_modules/.bin"
109
+ }
110
+
111
+ provision_python() {
112
+ local dir="$1"
113
+ local venv_rel="${HARNESS_PYTHON_VENV_DIR:-.har/venv}"
114
+ local venv_dir="$dir/$venv_rel"
115
+ local python_bin="python3"
116
+
117
+ if ! command -v python3 >/dev/null 2>&1; then
118
+ pt_log "python3 not found on PATH — skipping venv provisioning"
119
+ append_env "HARNESS_ECOSYSTEM" "python"
120
+ append_env "PYTHON_BIN" "${PYTHON_BIN:-python3}"
121
+ return
122
+ fi
123
+
124
+ if [ ! -d "$venv_dir" ]; then
125
+ pt_log "Creating Python venv at $venv_rel..."
126
+ if ! python3 -m venv "$venv_dir"; then
127
+ rm -rf "$venv_dir"
128
+ pt_log "python3 -m venv failed (on Debian/Ubuntu install python3-venv) — using system python3"
129
+ append_env "HARNESS_ECOSYSTEM" "python"
130
+ append_env "PYTHON_BIN" "$(command -v python3)"
131
+ return
132
+ fi
133
+ fi
134
+
135
+ if [ ! -x "$venv_dir/bin/python" ]; then
136
+ pt_log "Python venv at $venv_rel is missing or broken — using system python3"
137
+ append_env "HARNESS_ECOSYSTEM" "python"
138
+ append_env "PYTHON_BIN" "$(command -v python3)"
139
+ return
140
+ fi
141
+
142
+ python_bin="$venv_dir/bin/python"
143
+ # shellcheck disable=SC1091
144
+ source "$venv_dir/bin/activate"
145
+
146
+ if ! run_install_cmd "$dir"; then
147
+ pt_log "Installing Python dependencies..."
148
+ if [ -f "$dir/pyproject.toml" ]; then
149
+ (cd "$dir" && pip install -q -e ".[dev]" 2>/dev/null) || (cd "$dir" && pip install -q -e .)
150
+ elif [ -f "$dir/requirements.txt" ]; then
151
+ (cd "$dir" && pip install -q -r requirements.txt)
152
+ elif [ -f "$dir/setup.py" ] || [ -f "$dir/setup.cfg" ]; then
153
+ (cd "$dir" && pip install -q -e .)
154
+ elif [ -f "$dir/Pipfile" ] && command -v pipenv >/dev/null 2>&1; then
155
+ (cd "$dir" && pipenv install --dev)
156
+ python_bin="$(cd "$dir" && pipenv --py)"
157
+ fi
158
+ fi
159
+
160
+ append_env "HARNESS_ECOSYSTEM" "python"
161
+ append_env "PYTHON_BIN" "$python_bin"
162
+ append_env "VIRTUAL_ENV" "$venv_dir"
163
+ append_path_prefix "$venv_dir/bin"
164
+ }
165
+
166
+ provision_go() {
167
+ local dir="$1"
168
+ if ! run_install_cmd "$dir"; then
169
+ if command -v go >/dev/null 2>&1; then
170
+ pt_log "Downloading Go modules..."
171
+ (cd "$dir" && go mod download)
172
+ else
173
+ pt_log "go not found on PATH — record paths only"
174
+ fi
175
+ fi
176
+ append_env "HARNESS_ECOSYSTEM" "go"
177
+ append_env "GO_BIN" "$(command -v go 2>/dev/null || echo go)"
178
+ if [ -n "${GOPATH:-}" ]; then append_env "GOPATH" "$GOPATH"; fi
179
+ if [ -n "${GOROOT:-}" ]; then append_env "GOROOT" "$GOROOT"; fi
180
+ }
181
+
182
+ provision_rust() {
183
+ local dir="$1"
184
+ if ! run_install_cmd "$dir"; then
185
+ if command -v cargo >/dev/null 2>&1; then
186
+ pt_log "Fetching Rust dependencies..."
187
+ (cd "$dir" && cargo fetch)
188
+ else
189
+ pt_log "cargo not found on PATH — record paths only"
190
+ fi
191
+ fi
192
+ append_env "HARNESS_ECOSYSTEM" "rust"
193
+ append_env "CARGO_BIN" "$(command -v cargo 2>/dev/null || echo cargo)"
194
+ append_env "RUSTC_BIN" "$(command -v rustc 2>/dev/null || echo rustc)"
195
+ }
196
+
197
+ provision_java() {
198
+ local dir="$1"
199
+ run_install_cmd "$dir" || true
200
+ append_env "HARNESS_ECOSYSTEM" "java"
201
+ if [ -n "${JAVA_HOME:-}" ]; then
202
+ append_env "JAVA_HOME" "$JAVA_HOME"
203
+ append_path_prefix "$JAVA_HOME/bin"
204
+ fi
205
+ if command -v mvn >/dev/null 2>&1; then
206
+ append_env "MVN_BIN" "$(command -v mvn)"
207
+ elif [ -f "$dir/gradlew" ]; then
208
+ append_env "GRADLE_BIN" "$dir/gradlew"
209
+ elif command -v gradle >/dev/null 2>&1; then
210
+ append_env "GRADLE_BIN" "$(command -v gradle)"
211
+ fi
212
+ }
213
+
214
+ provision_ruby() {
215
+ local dir="$1"
216
+ if ! run_install_cmd "$dir"; then
217
+ if command -v bundle >/dev/null 2>&1; then
218
+ pt_log "Installing Ruby gems..."
219
+ (cd "$dir" && bundle install --quiet)
220
+ else
221
+ pt_log "bundle not found on PATH — record paths only"
222
+ fi
223
+ fi
224
+ append_env "HARNESS_ECOSYSTEM" "ruby"
225
+ append_env "RUBY_BIN" "$(command -v ruby 2>/dev/null || echo ruby)"
226
+ append_env "BUNDLE_BIN" "$(command -v bundle 2>/dev/null || echo bundle)"
227
+ append_path_prefix "$dir/vendor/bundle/bin"
228
+ }
229
+
230
+ provision_ios() {
231
+ local dir="$1"
232
+ run_install_cmd "$dir" || true
233
+ append_env "HARNESS_ECOSYSTEM" "ios"
234
+ append_env "XCODEBUILD_BIN" "$(command -v xcodebuild 2>/dev/null || echo xcodebuild)"
235
+ if [ -n "${HARNESS_XCODE_SCHEME:-}" ]; then
236
+ append_env "HARNESS_XCODE_SCHEME" "$HARNESS_XCODE_SCHEME"
237
+ fi
238
+ if [ -n "${HARNESS_SIMULATOR_NAME:-}" ]; then
239
+ append_env "HARNESS_SIMULATOR_NAME" "$HARNESS_SIMULATOR_NAME"
240
+ fi
241
+ if [ -n "${HARNESS_BUNDLE_ID:-}" ]; then
242
+ append_env "HARNESS_BUNDLE_ID" "$HARNESS_BUNDLE_ID"
243
+ fi
244
+ if [ -n "${DEVELOPER_DIR:-}" ]; then
245
+ append_env "DEVELOPER_DIR" "$DEVELOPER_DIR"
246
+ fi
247
+ }
248
+
249
+ provision_monorepo_root() {
250
+ [ -n "$HAR_REL_PREFIX" ] || return 0
251
+ [ -n "$HAR_WORKTREE_DIR" ] || return 0
252
+ [ -f "$HAR_WORKTREE_DIR/package.json" ] || return 0
253
+ [ -d "$HAR_WORKTREE_DIR/node_modules" ] && return 0
254
+ pt_log "Installing monorepo root dependencies in $HAR_WORKTREE_DIR..."
255
+ (cd "$HAR_WORKTREE_DIR" && npm install --silent)
256
+ }
257
+
258
+ provision_ecosystem() {
259
+ local dir="$1"
260
+ local ecosystem
261
+ ecosystem="$(detect_ecosystem "$dir")"
262
+ pt_log "Toolchain ecosystem: ${ecosystem} (work dir: ${dir})"
263
+
264
+ case "$ecosystem" in
265
+ node) provision_node "$dir" ;;
266
+ python) provision_python "$dir" ;;
267
+ go) provision_go "$dir" ;;
268
+ rust) provision_rust "$dir" ;;
269
+ java) provision_java "$dir" ;;
270
+ ruby) provision_ruby "$dir" ;;
271
+ ios) provision_ios "$dir" ;;
272
+ none)
273
+ if run_install_cmd "$dir"; then
274
+ append_env "HARNESS_ECOSYSTEM" "custom"
275
+ else
276
+ pt_log "No ecosystem manifest detected — set HARNESS_ECOSYSTEM or HARNESS_INSTALL_CMD in harness.env"
277
+ append_env "HARNESS_ECOSYSTEM" "none"
278
+ fi
279
+ ;;
280
+ esac
281
+ }
282
+
283
+ append_env "HARNESS_TOOLCHAIN_PROVISIONED" "true"
284
+ provision_ecosystem "$HAR_WORK_DIR"
285
+ provision_monorepo_root
@@ -4,9 +4,9 @@
4
4
  #
5
5
  # Usage: ./.har/verify.sh <agent-id> [--full]
6
6
  #
7
- # Quick (default): smoke compile / typecheck / health only
8
- # Full (--full): + unit tests, lint, optional readiness + browser-e2e
9
- # Step lists are examples not exhaustive. Adapt commands to this repo's stack.
7
+ # Quick (default): ecosystem smoke + health only
8
+ # Full (--full): + conventional tests, lint, optional readiness + browser-e2e
9
+ # Stock steps are examples. Replace them during adaptation to match this repo.
10
10
  set -euo pipefail
11
11
 
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -136,18 +136,98 @@ process.stdout.write(JSON.stringify(arr));
136
136
  fi
137
137
  }
138
138
 
139
- # ── Verification stages ─────────────────────────────────────────────────────
140
- # Customize these steps for your project — lists below are examples, not exhaustive.
141
- # Edit this section directly — do not use a separate config file.
139
+ node_package_script_exists() {
140
+ local script="$1"
141
+ "${NODE_BIN:-node}" -e "
142
+ const fs = require('fs');
143
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
144
+ const script = process.argv[1];
145
+ process.exit(pkg.scripts && pkg.scripts[script] ? 0 : 1);
146
+ " "$script"
147
+ }
142
148
 
143
- # Quick (default): smoke — prove the slot can compile/load, not full test suites.
144
- run_step "typecheck" "echo 'TODO: npm run typecheck'" || { [ -z "$FULL" ] && true; }
149
+ run_node_script_or_skip() {
150
+ local name="$1"
151
+ local script="$2"
152
+ if node_package_script_exists "$script"; then
153
+ run_step "$name" "${NPM_BIN:-npm} run ${script}"
154
+ else
155
+ run_step "$name" "echo 'No ${script} script configured; skipping.'"
156
+ fi
157
+ }
158
+
159
+ run_node_quick_smoke() {
160
+ if node_package_script_exists typecheck; then
161
+ run_step "node-typecheck" '${NPM_BIN:-npm} run typecheck'
162
+ elif node_package_script_exists build; then
163
+ run_step "node-build" '${NPM_BIN:-npm} run build'
164
+ else
165
+ run_step "node-package-load" '${NODE_BIN:-node} -e "require(\"./package.json\")"'
166
+ fi
167
+ }
168
+
169
+ run_quick_smoke() {
170
+ case "${HARNESS_ECOSYSTEM:-none}" in
171
+ node)
172
+ run_node_quick_smoke
173
+ ;;
174
+ python)
175
+ run_step "python-compile" '${PYTHON_BIN:-python3} -m compileall -q .'
176
+ ;;
177
+ go)
178
+ run_step "go-build" '${GO_BIN:-go} build ./...'
179
+ ;;
180
+ rust)
181
+ run_step "rust-check" '${CARGO_BIN:-cargo} check'
182
+ ;;
183
+ java)
184
+ run_step "java-compile" 'if [ -x ./mvnw ]; then ./mvnw -q -DskipTests compile; elif command -v mvn >/dev/null 2>&1; then mvn -q -DskipTests compile; elif [ -x ./gradlew ]; then ./gradlew classes; elif command -v gradle >/dev/null 2>&1; then gradle classes; else echo "No Maven/Gradle command found; adapt verify.sh for this Java repo."; fi'
185
+ ;;
186
+ ruby)
187
+ run_step "ruby-smoke" '${RUBY_BIN:-ruby} -e "puts RUBY_VERSION"'
188
+ ;;
189
+ custom|none|*)
190
+ run_step "smoke-not-configured" 'echo "No stock smoke for HARNESS_ECOSYSTEM=${HARNESS_ECOSYSTEM:-none}; adapt .har/verify.sh for this repo."'
191
+ ;;
192
+ esac
193
+ }
194
+
195
+ run_full_checks() {
196
+ case "${HARNESS_ECOSYSTEM:-none}" in
197
+ node)
198
+ run_node_script_or_skip "unit-tests" test || true
199
+ run_node_script_or_skip "lint" lint || true
200
+ ;;
201
+ python)
202
+ run_step "unit-tests" 'if ${PYTHON_BIN:-python3} -c "import pytest" >/dev/null 2>&1; then ${PYTHON_BIN:-python3} -m pytest -q; else echo "pytest not installed; adapt verify.sh for this Python repo."; fi' || true
203
+ ;;
204
+ go)
205
+ run_step "unit-tests" '${GO_BIN:-go} test ./...' || true
206
+ ;;
207
+ rust)
208
+ run_step "unit-tests" '${CARGO_BIN:-cargo} test' || true
209
+ ;;
210
+ java)
211
+ run_step "unit-tests" 'if [ -x ./mvnw ]; then ./mvnw -q test; elif command -v mvn >/dev/null 2>&1; then mvn -q test; elif [ -x ./gradlew ]; then ./gradlew test; elif command -v gradle >/dev/null 2>&1; then gradle test; else echo "No Maven/Gradle command found; adapt verify.sh for this Java repo."; fi' || true
212
+ ;;
213
+ ruby)
214
+ run_step "unit-tests" 'if command -v "${BUNDLE_BIN:-bundle}" >/dev/null 2>&1 && [ -f Gemfile ]; then "${BUNDLE_BIN:-bundle}" exec rake test 2>/dev/null || "${BUNDLE_BIN:-bundle}" exec rspec; else echo "No Ruby test command detected; adapt verify.sh for this Ruby repo."; fi' || true
215
+ ;;
216
+ custom|none|*)
217
+ run_step "unit-tests" 'echo "No stock full checks for HARNESS_ECOSYSTEM=${HARNESS_ECOSYSTEM:-none}; adapt .har/verify.sh for this repo."' || true
218
+ ;;
219
+ esac
220
+ }
221
+
222
+ # ── Verification stages ─────────────────────────────────────────────────────
223
+ # These stock steps are intentionally generic conventions. Adapt this section
224
+ # to the repository's real commands from package.json, Makefile, CI, pyproject,
225
+ # Cargo.toml, go.mod, pom.xml, etc.
226
+ run_quick_smoke || { [ -z "$FULL" ] && true; }
145
227
  run_http_step "api-health" "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}" || { [ -z "$FULL" ] && true; }
146
228
 
147
229
  if [ -n "$FULL" ]; then
148
- # Full: project-specific checks — add/remove/reorder steps for this repo.
149
- run_step "unit-tests" "echo 'TODO: npm test'" || true
150
- run_step "lint" "echo 'TODO: npm run lint'" || true
230
+ run_full_checks
151
231
  run_step "readiness" "run_readiness_if_configured \"$AGENT_ID\"" || true
152
232
  run_step "browser-e2e" "run_browser_e2e_if_present \"$SCRIPT_DIR\" \"$AGENT_ID\"" || true
153
233
  fi