@osfactory/har 0.1.1

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.
Files changed (71) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +208 -0
  3. package/control/docker-compose.build.yml +7 -0
  4. package/control/docker-compose.yml +31 -0
  5. package/dist/index.js +41753 -0
  6. package/dist/prompts/system-authoring.md +104 -0
  7. package/dist/templates/AGENT.md.template +40 -0
  8. package/dist/templates/CLAUDE.md.template +5 -0
  9. package/dist/templates/adaptation-prompt-init.md +124 -0
  10. package/dist/templates/adaptation-prompt-maintain.md +73 -0
  11. package/dist/templates/cursor-rule.mdc.template +101 -0
  12. package/dist/templates/har-boilerplate/CLAUDE.agent.md +54 -0
  13. package/dist/templates/har-boilerplate/README.md +134 -0
  14. package/dist/templates/har-boilerplate/agent-cli.sh +172 -0
  15. package/dist/templates/har-boilerplate/agent-slot.sh +270 -0
  16. package/dist/templates/har-boilerplate/attach.sh +16 -0
  17. package/dist/templates/har-boilerplate/docker-compose.agent.yml +66 -0
  18. package/dist/templates/har-boilerplate/ecosystem.agent.template.cjs +44 -0
  19. package/dist/templates/har-boilerplate/env.template +37 -0
  20. package/dist/templates/har-boilerplate/harness.env +50 -0
  21. package/dist/templates/har-boilerplate/justfile +39 -0
  22. package/dist/templates/har-boilerplate/launch.sh +229 -0
  23. package/dist/templates/har-boilerplate/setup-infra.sh +102 -0
  24. package/dist/templates/har-boilerplate/stages/README.sh +9 -0
  25. package/dist/templates/har-boilerplate/stages.json +67 -0
  26. package/dist/templates/har-boilerplate/teardown.sh +86 -0
  27. package/dist/templates/har-boilerplate/verify.sh +163 -0
  28. package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +41 -0
  29. package/dist/templates/har-boilerplate-cli/README.md +112 -0
  30. package/dist/templates/har-boilerplate-cli/agent-cli.sh +162 -0
  31. package/dist/templates/har-boilerplate-cli/agent-slot.sh +270 -0
  32. package/dist/templates/har-boilerplate-cli/docker-compose.agent.yml +64 -0
  33. package/dist/templates/har-boilerplate-cli/harness.env +40 -0
  34. package/dist/templates/har-boilerplate-cli/justfile +27 -0
  35. package/dist/templates/har-boilerplate-cli/launch.sh +132 -0
  36. package/dist/templates/har-boilerplate-cli/setup-infra.sh +102 -0
  37. package/dist/templates/har-boilerplate-cli/stages/README.sh +9 -0
  38. package/dist/templates/har-boilerplate-cli/stages.json +61 -0
  39. package/dist/templates/har-boilerplate-cli/teardown.sh +66 -0
  40. package/dist/templates/har-boilerplate-cli/verify.sh +113 -0
  41. package/dist/templates/har-boilerplate-ios/ADAPT-PROMPT.md +57 -0
  42. package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +65 -0
  43. package/dist/templates/har-boilerplate-ios/README.md +88 -0
  44. package/dist/templates/har-boilerplate-ios/agent-cli.sh +125 -0
  45. package/dist/templates/har-boilerplate-ios/agent-slot.sh +270 -0
  46. package/dist/templates/har-boilerplate-ios/docker-compose.agent.yml +17 -0
  47. package/dist/templates/har-boilerplate-ios/harness.env +42 -0
  48. package/dist/templates/har-boilerplate-ios/justfile +33 -0
  49. package/dist/templates/har-boilerplate-ios/launch.sh +132 -0
  50. package/dist/templates/har-boilerplate-ios/setup-infra.sh +84 -0
  51. package/dist/templates/har-boilerplate-ios/stages/README.sh +12 -0
  52. package/dist/templates/har-boilerplate-ios/stages.json +61 -0
  53. package/dist/templates/har-boilerplate-ios/teardown.sh +64 -0
  54. package/dist/templates/har-boilerplate-ios/verify.sh +165 -0
  55. package/dist/templates/stage-templates/playwright/.github/workflows/playwright.yml +30 -0
  56. package/dist/templates/stage-templates/playwright/.har/stages/PLAYWRIGHT.md +21 -0
  57. package/dist/templates/stage-templates/playwright/.har/stages/browser-e2e.sh +83 -0
  58. package/dist/templates/stage-templates/playwright/README.md +14 -0
  59. package/dist/templates/stage-templates/playwright/package.fragment.json +14 -0
  60. package/dist/templates/stage-templates/playwright/playwright.config.js +30 -0
  61. package/dist/templates/stage-templates/playwright/template.manifest.json +44 -0
  62. package/dist/templates/stage-templates/playwright/tests/a11y/smoke.spec.js +14 -0
  63. package/dist/templates/stage-templates/playwright/tests/api/health.spec.js +10 -0
  64. package/dist/templates/stage-templates/playwright/tests/frontend/smoke.spec.js +9 -0
  65. package/dist/templates/stage-templates/rocketsim/.har/stages/ROCKETSIM.md +126 -0
  66. package/dist/templates/stage-templates/rocketsim/.har/stages/rocketsim-flows.sh +188 -0
  67. package/dist/templates/stage-templates/rocketsim/README.md +66 -0
  68. package/dist/templates/stage-templates/rocketsim/flows/README.md +36 -0
  69. package/dist/templates/stage-templates/rocketsim/flows/example-smoke.sh +60 -0
  70. package/dist/templates/stage-templates/rocketsim/template.manifest.json +38 -0
  71. package/package.json +78 -0
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env bash
2
+ # Progressive verification pipeline for an agent environment.
3
+ # Outputs JSON to stdout, human-readable progress to stderr.
4
+ #
5
+ # Usage: ./.har/verify.sh <agent-id> [--full]
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
10
+
11
+ # shellcheck source=/dev/null
12
+ source "$SCRIPT_DIR/harness.env"
13
+ # shellcheck source=/dev/null
14
+ source "$SCRIPT_DIR/agent-slot.sh"
15
+
16
+ AGENT_ID="${1:?Usage: verify.sh <agent-id> [--full]}"
17
+ FULL=""
18
+
19
+ for arg in "${@:2}"; do
20
+ [ "$arg" = "--full" ] && FULL=1
21
+ done
22
+
23
+ validate_agent_id "$AGENT_ID"
24
+
25
+ API_PORT=$(( HARNESS_API_BASE_PORT + AGENT_ID * 10 ))
26
+
27
+ ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
28
+ echo "No .env.agent.${AGENT_ID} found. Run: ./.har/launch.sh ${AGENT_ID}" >&2
29
+ exit 1
30
+ }
31
+
32
+ set -a
33
+ # shellcheck source=/dev/null
34
+ source "$ENV_FILE"
35
+ set +a
36
+
37
+ WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
38
+ API_PORT="${API_PORT:-$(( HARNESS_API_BASE_PORT + AGENT_ID * 10 ))}"
39
+
40
+ echo "==> Verifying agent ${AGENT_ID} (work dir: ${WORK_DIR})..." >&2
41
+
42
+ OVERALL_PASS=true
43
+ START_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
44
+ RESULTS_JSON="[]"
45
+
46
+ run_step() {
47
+ local name="$1"
48
+ local cmd="$2"
49
+ local start end elapsed exit_code output
50
+
51
+ printf " → %-40s" "$name..." >&2
52
+ start=$(date +%s%3N 2>/dev/null || echo "0")
53
+
54
+ set +e
55
+ output=$(cd "$WORK_DIR" && set -a && . "$ENV_FILE" && set +a && eval "$cmd" 2>&1)
56
+ exit_code=$?
57
+ set -e
58
+
59
+ end=$(date +%s%3N 2>/dev/null || echo "0")
60
+ elapsed=$(( end - start ))
61
+
62
+ local pass_bool step_output_escaped
63
+ if [ "$exit_code" = "0" ]; then
64
+ echo "✓ (${elapsed}ms)" >&2
65
+ pass_bool="true"
66
+ else
67
+ echo "✗ (${elapsed}ms)" >&2
68
+ echo "$output" | head -30 | sed 's/^/ /' >&2
69
+ pass_bool="false"
70
+ OVERALL_PASS=false
71
+ fi
72
+
73
+ step_output_escaped=$(echo "$output" | head -50 | \
74
+ node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(d.trim())))" \
75
+ 2>/dev/null || echo '""')
76
+
77
+ RESULTS_JSON=$(echo "$RESULTS_JSON" | node -e "
78
+ const fs = require('fs');
79
+ let arr = JSON.parse(fs.readFileSync('/dev/stdin','utf8'));
80
+ arr.push({name:'$name',pass:$pass_bool,ms:$elapsed,output:$step_output_escaped});
81
+ process.stdout.write(JSON.stringify(arr));
82
+ " 2>/dev/null || echo "$RESULTS_JSON")
83
+
84
+ if [ "$pass_bool" = "false" ] && [ -z "$FULL" ]; then
85
+ return 1
86
+ fi
87
+ }
88
+
89
+ run_http_step() {
90
+ local name="$1"
91
+ local url="$2"
92
+ local start end elapsed exit_code output
93
+
94
+ printf " → %-40s" "$name..." >&2
95
+ start=$(date +%s%3N 2>/dev/null || echo "0")
96
+
97
+ set +e
98
+ output=$(curl -sf "$url" 2>&1)
99
+ exit_code=$?
100
+ set -e
101
+
102
+ end=$(date +%s%3N 2>/dev/null || echo "0")
103
+ elapsed=$(( end - start ))
104
+
105
+ local pass_bool step_output_escaped
106
+ if [ "$exit_code" = "0" ]; then
107
+ echo "✓ (${elapsed}ms)" >&2
108
+ pass_bool="true"
109
+ else
110
+ echo "✗ (${elapsed}ms)" >&2
111
+ pass_bool="false"
112
+ OVERALL_PASS=false
113
+ fi
114
+
115
+ step_output_escaped=$(echo "$output" | head -50 | \
116
+ node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(d.trim())))" \
117
+ 2>/dev/null || echo '""')
118
+
119
+ RESULTS_JSON=$(echo "$RESULTS_JSON" | node -e "
120
+ const fs = require('fs');
121
+ let arr = JSON.parse(fs.readFileSync('/dev/stdin','utf8'));
122
+ arr.push({name:'$name',pass:$pass_bool,ms:$elapsed,output:$step_output_escaped});
123
+ process.stdout.write(JSON.stringify(arr));
124
+ " 2>/dev/null || echo "$RESULTS_JSON")
125
+
126
+ if [ "$pass_bool" = "false" ] && [ -z "$FULL" ]; then
127
+ return 1
128
+ fi
129
+ }
130
+
131
+ # ── Verification stages ─────────────────────────────────────────────────────
132
+ # TODO: Customize these steps for your project.
133
+ # Edit this section directly — do not use a separate config file.
134
+
135
+ run_step "typecheck" "echo 'TODO: npm run typecheck'" || { [ -z "$FULL" ] && true; }
136
+ run_step "unit-tests" "echo 'TODO: npm test'" || { [ -z "$FULL" ] && true; }
137
+ run_http_step "api-health" "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}" || { [ -z "$FULL" ] && true; }
138
+
139
+ if [ -n "$FULL" ]; then
140
+ run_step "lint" "echo 'TODO: npm run lint'" || true
141
+ run_step "browser-e2e" "run_browser_e2e_if_present \"$SCRIPT_DIR\" \"$AGENT_ID\"" || true
142
+ fi
143
+
144
+ # ── Output results ────────────────────────────────────────────────────────────
145
+
146
+ END_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
147
+ TOTAL_MS=$(( END_TOTAL - START_TOTAL ))
148
+
149
+ node -e "
150
+ const results = $RESULTS_JSON;
151
+ const overall = results.length > 0 && results.every(r => r.pass);
152
+ const out = {
153
+ status: overall ? 'pass' : 'fail',
154
+ agent_id: $AGENT_ID,
155
+ total_ms: $TOTAL_MS,
156
+ stages: results,
157
+ };
158
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
159
+ " 2>/dev/null || echo "{\"status\":\"fail\",\"agent_id\":${AGENT_ID},\"stages\":[]}"
160
+
161
+ if [ "$OVERALL_PASS" = "false" ]; then
162
+ exit 1
163
+ fi
@@ -0,0 +1,41 @@
1
+ # Agent ${AGENT_ID} — Development Environment
2
+
3
+ > [`AGENT.md`](../AGENT.md) · [`.har/README.md`](./README.md) · [`stages.json`](./stages.json)
4
+
5
+ ## Environment
6
+
7
+ | | |
8
+ |--|--|
9
+ | **Agent ID** | ${AGENT_ID} |
10
+ | **Work dir** | Fresh session worktree per launch — see the launch output or `.har/slots/agent-${AGENT_ID}.json` |
11
+
12
+ **Never edit the main checkout** — launch FIRST, then make ALL file edits under the work dir from the launch output. Relaunching replaces the session (branch kept) and requires explicit confirmation (`--replace` / `confirmReplace`); dirty worktrees also need `--force` after user approval — never autonomously.
13
+
14
+ ```bash
15
+ ./.har/agent-cli.sh ${AGENT_ID} status
16
+ ```
17
+
18
+ ## Definition of done
19
+
20
+ - [ ] Full verification returns `"status": "pass"` (`har env verify ${AGENT_ID} --full`, MCP `har_run_verification` with `full: true`, or `./.har/verify.sh ${AGENT_ID} --full`)
21
+ - [ ] When `stages/browser-e2e.sh` exists, full verify includes Playwright — adapt specs under `tests/` for UI changes
22
+ - [ ] New behavior has automated test coverage
23
+ - [ ] Changes committed **in the session worktree** with a clear message
24
+ - [ ] Finish with `har env complete ${AGENT_ID}` (or MCP `har_complete_environment`) — records the validation and tears down while **keeping the session branch** for the user to push / open a PR
25
+
26
+ Quick loop: MCP `har_run_verification`, `har env verify ${AGENT_ID}`, or `./.har/verify.sh ${AGENT_ID}`
27
+
28
+ ## Project commands
29
+
30
+ ```bash
31
+ # TODO: adapt for this repository
32
+ # npm run typecheck
33
+ # npm test
34
+ ```
35
+
36
+ ## Do not
37
+
38
+ - Work around a failing harness command with ad-hoc setup — fix the harness or report the failure
39
+ - Edit `.env.agent.${AGENT_ID}` by hand
40
+ - Run `verify` before `launch` when e2e needs a running server
41
+ - Edit the main checkout — all edits go under the session work dir
@@ -0,0 +1,112 @@
1
+ # .har — Agent Harness (CLI / library profile)
2
+
3
+ This directory is the **agent harness** for this repository. It lets AI coding agents run the project in isolated git worktrees with optional Docker-backed shared infra.
4
+
5
+ Generated and maintained by [`har`](https://github.com/antoineFrau/har). Run `har env maintain` when the repo stack changes.
6
+
7
+ **The harness is how you run this project.** Launch a slot to exercise the code in isolation; don't hand-roll setup. If a harness command fails, fix the harness or report it — don't silently fall back to ad-hoc commands.
8
+
9
+ ## What's in here
10
+
11
+ | File | Purpose |
12
+ |------|---------|
13
+ | `README.md` | This file — index of the harness |
14
+ | `manifest.json` | Generator metadata (version, profile, checksums) — do not edit |
15
+ | `harness.env` | Shared config: worktree default, `HARNESS_INFRA_SERVICES`, migrate/seed commands |
16
+ | `stages.json` | Machine-readable registry of runnable harness stages |
17
+ | `stages/` | Optional custom stage scripts registered from `stages.json` |
18
+ | `runs/` | Run history from `har env` / MCP only — `.har/runs/YYYY-MM-DD/HH-mm-ss_<stageId>_agent-<id>.json` (gitignore) |
19
+ | `artifacts/` | Stage outputs: reports, traces, screenshots, logs |
20
+ | `agent-slot.sh` | Shared agent-id validation (reads limits from `harness.env`) |
21
+ | `setup-infra.sh` | Start optional Docker Compose stack + template database |
22
+ | `launch.sh` | Launch one agent slot (git worktree by default, deps, env file) |
23
+ | `verify.sh` | Verification pipeline (typecheck, tests, lint, build) |
24
+ | `teardown.sh` | Tear down one agent slot (worktree + env file) |
25
+ | `agent-cli.sh` | Inspect slot status, run commands in the work dir |
26
+ | `docker-compose.agent.yml` | Shared infrastructure containers (services listed in `HARNESS_INFRA_SERVICES`) |
27
+ | `CLAUDE.agent.md` | Detailed instructions for coding agents |
28
+ | `justfile` | Optional shortcuts (requires `just`) |
29
+
30
+ No PM2 or `ecosystem.agent.template.cjs` in this profile — agents run project commands directly in their worktree.
31
+
32
+ ## Quick start
33
+
34
+ **Preferred — har CLI or MCP** (persists run history under `.har/runs/`):
35
+
36
+ ```bash
37
+ har env launch 1
38
+ har env verify 1
39
+ har env verify 1 --full
40
+ har env teardown 1
41
+ ```
42
+
43
+ In Cursor with HAR MCP configured: use `har_launch_environment`, `har_run_verification`, and `har_teardown_environment`.
44
+
45
+ **Shell fallback** (no CLI/MCP installed):
46
+
47
+ ```bash
48
+ ./.har/setup-infra.sh # when HARNESS_INFRA_SERVICES is non-empty
49
+ ./.har/launch.sh 1
50
+ ./.har/verify.sh 1
51
+ ./.har/verify.sh 1 --full # + lint, build, browser-e2e (if Playwright installed)
52
+ ./.har/teardown.sh 1
53
+ ```
54
+
55
+ Read **`stages.json`** and **`verificationStages`**. Optional: `har env add-stage playwright`.
56
+
57
+ ## Verification contract
58
+
59
+ | Mode | Command | Typical steps |
60
+ |------|---------|---------------|
61
+ | Quick | `har env verify <id>` or `verify.sh <id>` | typecheck, unit tests |
62
+ | Full | `har env verify <id> --full` or `verify.sh <id> --full` | + lint, build, **browser-e2e** when `stages/browser-e2e.sh` exists |
63
+
64
+ Use `har env launch 1 --no-worktree` or `./.har/launch.sh 1 --no-worktree` only when working in the repo root.
65
+
66
+ ## Run history
67
+
68
+ | Entry point | Writes `.har/runs/`? |
69
+ |-------------|------------------------|
70
+ | `./.har/*.sh` | No |
71
+ | `har env …` / MCP | Yes — main checkout `.har/runs/YYYY-MM-DD/` |
72
+
73
+ With worktree slots, tests run in the worktree; run JSON lives in the main repo. See `workDir` in each record.
74
+
75
+ ## For coding agents
76
+
77
+ **Start here:** read [`AGENT.md`](../AGENT.md) at the repo root for a short pointer, then [`.har/CLAUDE.agent.md`](./CLAUDE.agent.md) for full instructions.
78
+
79
+ Prefer HAR MCP tools or `har env …` for launch, verify, and teardown. Use `./.har/*.sh` only when the CLI is not installed.
80
+
81
+ Work in the isolated git worktree created by launch. Use `./.har/agent-cli.sh <id> exec ...` to run ad-hoc project commands in that work dir.
82
+
83
+ When the project needs Postgres, Redis, or similar, add the service to `docker-compose.agent.yml` (or keep one from the menu), list it in `HARNESS_INFRA_SERVICES` in `harness.env`, and use `setup-infra.sh` — never run raw `docker compose` for shared infra. Shared services run once on fixed ports and serve every agent slot.
84
+
85
+ ## Maintaining this harness
86
+
87
+ When the project stack changes (new test commands, database needs, env vars):
88
+
89
+ ```bash
90
+ har env maintain
91
+ ```
92
+
93
+ The authoring agent updates scripts and this README. Review changes before committing.
94
+
95
+ **Do not** put runtime behavior in YAML — edit the scripts directly.
96
+
97
+ ## Session lifecycle
98
+
99
+ Every `launch` starts a **fresh session**: a new git worktree from the current HEAD at
100
+ `~/worktrees/<base-branch>-<sha4>-har-agent-<id>-<rand4>`, on a branch of the same name.
101
+ The session is recorded in `.har/slots/agent-<id>.json` (the slot registry) — status,
102
+ verify, and teardown resolve the work dir through it. Make ALL file edits under the
103
+ work dir printed by launch, never in the main checkout.
104
+
105
+ - Relaunching a slot **replaces** its previous session; replacement requires `--replace` /
106
+ `confirmReplace=true` (or an interactive prompt). Uncommitted changes also need `--force`
107
+ after explicit user approval.
108
+ - `teardown` removes the worktree but **keeps the session branch** so you can push it
109
+ or open a PR (`--delete-branch` to drop it).
110
+ - `har env complete <id>` finishes a session: full verify (recorded as a validation),
111
+ then teardown — branch kept.
112
+ - `--no-worktree` runs the slot from the repo root instead (single-agent mode).
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env bash
2
+ # Namespaced CLI for managing a CLI/library agent slot.
3
+ #
4
+ # Usage: ./.har/agent-cli.sh <agent-id> <command> [args...]
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
9
+
10
+ # shellcheck source=/dev/null
11
+ source "$SCRIPT_DIR/harness.env"
12
+ # shellcheck source=/dev/null
13
+ source "$SCRIPT_DIR/agent-slot.sh"
14
+
15
+ AGENT_ID="${1:?Usage: agent-cli.sh <agent-id> <command> [args...]}"
16
+ COMMAND="${2:?Usage: agent-cli.sh <agent-id> <command> [args...]}"
17
+
18
+ validate_agent_id "$AGENT_ID"
19
+
20
+ WORKTREE_DIR="$(existing_slot_worktree "$AGENT_ID")"
21
+ DB_PORT="${AGENT_DB_PORT:-15432}"
22
+ export PGPASSWORD="password"
23
+
24
+ resolve_work_dir() {
25
+ local env_file
26
+ env_file="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
27
+ echo "No active environment for agent ${AGENT_ID}" >&2
28
+ echo " Run: ./.har/launch.sh ${AGENT_ID}" >&2
29
+ exit 1
30
+ }
31
+ # shellcheck source=/dev/null
32
+ source "$env_file"
33
+ resolve_agent_work_dir "$env_file" "$AGENT_ID"
34
+ }
35
+
36
+ case "$COMMAND" in
37
+ status)
38
+ ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT" || true)"
39
+ REGISTRY_FILE="$(slot_registry_file "$AGENT_ID")"
40
+
41
+ if [ -n "$ENV_FILE" ]; then
42
+ # shellcheck source=/dev/null
43
+ source "$ENV_FILE"
44
+ WT="$(existing_slot_worktree "$AGENT_ID")"
45
+ echo "Agent ${AGENT_ID}: active"
46
+ echo " Work dir: $(resolve_agent_work_dir "$ENV_FILE" "$AGENT_ID")"
47
+ [ -n "$WT" ] && [ -d "$WT" ] && echo " Worktree: $WT"
48
+ BRANCH="$(read_slot_field "$REGISTRY_FILE" branch || true)"
49
+ PURPOSE="$(read_slot_field "$REGISTRY_FILE" purpose || true)"
50
+ CREATED="$(read_slot_field "$REGISTRY_FILE" createdAt || true)"
51
+ [ -n "$PURPOSE" ] && echo " Purpose: $PURPOSE"
52
+ [ -n "$BRANCH" ] && echo " Branch: $BRANCH"
53
+ [ -n "$CREATED" ] && echo " Since: $CREATED"
54
+ [ -n "$WT" ] && echo " Git: $(slot_dirty_summary "$WT")"
55
+ else
56
+ echo "No active environment for agent ${AGENT_ID}"
57
+ echo " Run: ./.har/launch.sh ${AGENT_ID}"
58
+ fi
59
+ ;;
60
+
61
+ logs)
62
+ echo "CLI profile has no managed processes (no PM2)." >&2
63
+ echo "Run project commands in the work dir, e.g.:" >&2
64
+ echo " ./.har/agent-cli.sh ${AGENT_ID} exec npm test" >&2
65
+ exit 1
66
+ ;;
67
+
68
+ restart)
69
+ echo "CLI profile has no managed processes to restart." >&2
70
+ echo "Re-run launch or use exec for project-specific commands." >&2
71
+ exit 1
72
+ ;;
73
+
74
+ psql)
75
+ QUERY="${3:-}"
76
+ if ! har_infra_enabled db; then
77
+ echo "PostgreSQL infra is disabled in harness.env" >&2
78
+ exit 1
79
+ fi
80
+ if [ -n "$QUERY" ]; then
81
+ har_pg psql -d "agent_${AGENT_ID}" -c "$QUERY"
82
+ else
83
+ har_pg psql -d "agent_${AGENT_ID}"
84
+ fi
85
+ ;;
86
+
87
+ health)
88
+ API_PORT=$(( HARNESS_API_BASE_PORT + AGENT_ID * 10 ))
89
+ if [ -n "${HARNESS_HEALTH_CHECK_PATH:-}" ]; then
90
+ curl -sf "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}" | node -e "
91
+ const d = require('fs').readFileSync('/dev/stdin','utf8');
92
+ try { console.log(JSON.stringify(JSON.parse(d), null, 2)); } catch { console.log(d); }
93
+ " || curl -v "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}"
94
+ else
95
+ echo "No health check path configured in harness.env"
96
+ fi
97
+ ;;
98
+
99
+ url)
100
+ FE_PORT=$(( HARNESS_FE_BASE_PORT + AGENT_ID * 10 ))
101
+ API_PORT=$(( HARNESS_API_BASE_PORT + AGENT_ID * 10 ))
102
+ WORK_DIR="$(resolve_work_dir 2>/dev/null || echo "$REPO_ROOT")"
103
+ echo "Work dir: $WORK_DIR"
104
+ [ -d "$WORKTREE_DIR" ] && echo "Worktree: $WORKTREE_DIR"
105
+ [ -n "${HARNESS_HEALTH_CHECK_PATH:-}" ] && echo "API: http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}"
106
+ har_infra_enabled db && echo "Database: agent_${AGENT_ID} @ localhost:${DB_PORT}"
107
+ har_infra_enabled minio && echo "MinIO: http://localhost:19001"
108
+ har_infra_enabled headless-browser && echo "Browser: http://localhost:13001"
109
+ har_infra_enabled mailpit && echo "Mailpit: http://localhost:18025"
110
+ ;;
111
+
112
+ reset-db)
113
+ if ! har_infra_enabled db; then
114
+ echo "PostgreSQL infra is disabled in harness.env" >&2
115
+ exit 1
116
+ fi
117
+ echo "==> Resetting database for agent ${AGENT_ID}..."
118
+ har_pg psql -d postgres -c \
119
+ "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='agent_${AGENT_ID}';" \
120
+ >/dev/null
121
+ har_pg dropdb --if-exists "agent_${AGENT_ID}"
122
+ har_pg createdb -T "$HARNESS_TEMPLATE_DB" "agent_${AGENT_ID}"
123
+ echo "✓ Database reset to clean state"
124
+ ;;
125
+
126
+ slow-queries)
127
+ if ! har_infra_enabled db; then
128
+ echo "PostgreSQL infra is disabled in harness.env" >&2
129
+ exit 1
130
+ fi
131
+ har_pg psql -d "agent_${AGENT_ID}" -c "
132
+ SELECT round(mean_exec_time::numeric, 2) AS mean_ms,
133
+ calls,
134
+ left(query, 120) AS query
135
+ FROM pg_stat_statements
136
+ ORDER BY mean_exec_time DESC
137
+ LIMIT 20;" 2>/dev/null || echo "pg_stat_statements extension not available"
138
+ ;;
139
+
140
+ exec)
141
+ shift 2
142
+ if [ $# -eq 0 ]; then
143
+ echo "Usage: agent-cli.sh ${AGENT_ID} exec <command>" >&2
144
+ exit 1
145
+ fi
146
+ WORK_DIR="$(resolve_work_dir)"
147
+ if har_infra_enabled db; then
148
+ PGHOST=localhost PGPORT="$DB_PORT" PGUSER=postgres PGDATABASE="agent_${AGENT_ID}" \
149
+ bash -c "cd '$WORK_DIR' && $*"
150
+ else
151
+ bash -c "cd '$WORK_DIR' && $*"
152
+ fi
153
+ ;;
154
+
155
+ *)
156
+ echo "Unknown command: $COMMAND" >&2
157
+ echo ""
158
+ echo "Commands: status, url, exec <cmd>"
159
+ har_infra_enabled db && echo " psql [query], reset-db, slow-queries"
160
+ exit 1
161
+ ;;
162
+ esac