@osfactory/har 0.64.2 → 1.0.0-rc.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.
- package/dist/index.js +15675 -6536
- package/dist/prompts/system-authoring.md +155 -0
- package/dist/templates/adaptation-prompt-init.md +67 -43
- package/dist/templates/adaptation-prompt-maintain.md +38 -38
- package/dist/templates/agent-skills/har-maintain.md +2 -2
- package/dist/templates/agent-skills/setup-har.md +6 -4
- package/dist/templates/cursor-rule.mdc.template +3 -3
- package/dist/templates/har-boilerplate/docker-compose.agent.yml +1 -1
- package/dist/templates/har-boilerplate/harness.env +11 -155
- package/dist/templates/har-boilerplate/stages.json +51 -2
- package/dist/templates/har-boilerplate-cli/docker-compose.agent.yml +1 -1
- package/dist/templates/har-boilerplate-cli/harness.env +12 -138
- package/dist/templates/har-boilerplate-cli/stages.json +42 -2
- package/dist/templates/har-boilerplate-ios/harness.env +10 -126
- package/dist/templates/har-boilerplate-ios/stages.json +42 -2
- package/dist/templates/migration-prompt.md +54 -0
- package/dist/templates/plugins/custom-stage-skeleton.sh +11 -20
- package/dist/templates/plugins/gitleaks/.github/workflows/gitleaks.yml +1 -1
- package/dist/templates/plugins/gitleaks/.har/stages/GITLEAKS.md +1 -1
- package/dist/templates/plugins/gitleaks/.har/stages/secrets-scan.sh +8 -23
- package/dist/templates/plugins/gitleaks/README.md +1 -1
- package/dist/templates/plugins/gitleaks/template.manifest.json +20 -5
- package/dist/templates/plugins/kerno/.github/workflows/kerno.yml +1 -1
- package/dist/templates/plugins/kerno/.har/stages/backend-validation.sh +8 -23
- package/dist/templates/plugins/kerno/README.md +1 -1
- package/dist/templates/plugins/kerno/template.manifest.json +20 -9
- package/dist/templates/plugins/local-plugin-skeleton/README.md +46 -0
- package/dist/templates/plugins/playwright/.har/stages/browser-e2e.sh +8 -23
- package/dist/templates/plugins/playwright/template.manifest.json +36 -13
- package/dist/templates/plugins/rocketsim/.har/stages/rocketsim-flows.sh +8 -23
- package/dist/templates/plugins/rocketsim/template.manifest.json +20 -8
- package/dist/templates/plugins/semgrep/.har/stages/sast.sh +10 -26
- package/dist/templates/plugins/semgrep/README.md +1 -1
- package/dist/templates/plugins/semgrep/template.manifest.json +16 -7
- package/dist/templates/plugins/trivy/.har/stages/vuln-scan.sh +8 -23
- package/dist/templates/plugins/trivy/README.md +1 -1
- package/dist/templates/plugins/trivy/template.manifest.json +20 -8
- package/dist/templates/{har-boilerplate-cli/CLAUDE.agent.md → profiles/cli/docs/claude-agent/definition-of-done.md} +0 -42
- package/dist/templates/profiles/cli/docs/claude-agent/do-not.md +6 -0
- package/dist/templates/profiles/cli/docs/claude-agent/environment.md +12 -0
- package/dist/templates/profiles/cli/docs/claude-agent/project-commands.md +10 -0
- package/dist/templates/profiles/cli/docs/claude-agent/readiness.md +6 -0
- package/dist/templates/profiles/cli/docs/readme/for-coding-agents.md +9 -0
- package/dist/templates/profiles/cli/docs/readme/intro.md +7 -0
- package/dist/templates/profiles/cli/docs/readme/port-shared-services-cli-profile.md +25 -0
- package/dist/templates/profiles/cli/docs/readme/quick-start.md +24 -0
- package/dist/templates/profiles/cli/docs/readme/verification-contract.md +22 -0
- package/dist/templates/profiles/cli/docs/readme/what-s-in-here.md +27 -0
- package/dist/templates/profiles/cli/profile.manifest.json +56 -1
- package/dist/templates/profiles/default/docs/claude-agent/definition-of-done.md +24 -0
- package/dist/templates/profiles/default/docs/claude-agent/do-not.md +10 -0
- package/dist/templates/profiles/default/docs/claude-agent/environment.md +19 -0
- package/dist/templates/profiles/default/docs/claude-agent/project-commands.md +10 -0
- package/dist/templates/profiles/default/docs/claude-agent/readiness.md +12 -0
- package/dist/templates/profiles/default/docs/readme/architecture.md +11 -0
- package/dist/templates/profiles/default/docs/readme/for-coding-agents.md +9 -0
- package/dist/templates/profiles/default/docs/readme/intro.md +7 -0
- package/dist/templates/profiles/default/docs/readme/port-shared-services.md +41 -0
- package/dist/templates/profiles/default/docs/readme/quick-start.md +24 -0
- package/dist/templates/profiles/default/docs/readme/readiness-layers.md +16 -0
- package/dist/templates/profiles/default/docs/readme/verification-contract.md +17 -0
- package/dist/templates/profiles/default/docs/readme/what-s-in-here.md +28 -0
- package/dist/templates/profiles/default/profile.manifest.json +62 -1
- package/dist/templates/profiles/ios/docs/claude-agent/definition-of-done.md +23 -0
- package/dist/templates/profiles/ios/docs/claude-agent/do-not.md +6 -0
- package/dist/templates/profiles/ios/docs/claude-agent/environment.md +14 -0
- package/dist/templates/profiles/ios/docs/claude-agent/intro.md +3 -0
- package/dist/templates/profiles/ios/docs/claude-agent/project-commands.md +21 -0
- package/dist/templates/profiles/ios/docs/claude-agent/readiness.md +6 -0
- package/dist/templates/profiles/ios/docs/claude-agent/user-flow-validation-rocketsim.md +11 -0
- package/dist/templates/profiles/ios/docs/readme/configuration.md +7 -0
- package/dist/templates/profiles/ios/docs/readme/intro.md +7 -0
- package/dist/templates/profiles/ios/docs/readme/port-shared-services-ios-profile.md +25 -0
- package/dist/templates/profiles/ios/docs/readme/quick-start.md +22 -0
- package/dist/templates/profiles/ios/docs/readme/simulators.md +45 -0
- package/dist/templates/profiles/ios/docs/readme/user-flow-validation.md +9 -0
- package/dist/templates/profiles/ios/docs/readme/verification-contract.md +13 -0
- package/dist/templates/profiles/ios/docs/readme/what-s-in-here.md +27 -0
- package/dist/templates/profiles/ios/profile.manifest.json +59 -3
- package/dist/templates/runtime-bundles/pm2-runtime/attach.sh +8 -15
- package/dist/templates/runtime-bundles/pm2-runtime/env.template +1 -1
- package/dist/templates/runtime-bundles/pm2-runtime/stages/api-health.sh +9 -0
- package/dist/templates/runtime-bundles/shared-kernel/STAGES.md +32 -18
- package/dist/templates/runtime-bundles/shared-kernel/agent-cli.sh +35 -0
- package/dist/templates/runtime-bundles/shared-kernel/launch.sh +35 -0
- package/dist/templates/runtime-bundles/shared-kernel/lib/verify-runner.mjs +129 -0
- package/dist/templates/runtime-bundles/shared-kernel/preflight.sh +35 -0
- package/dist/templates/runtime-bundles/shared-kernel/setup-infra.sh +35 -0
- package/dist/templates/runtime-bundles/shared-kernel/stages/README.sh +1 -1
- package/dist/templates/runtime-bundles/shared-kernel/stages/readiness.sh +14 -0
- package/dist/templates/runtime-bundles/shared-kernel/teardown.sh +35 -0
- package/dist/templates/runtime-bundles/shared-kernel/verify.sh +35 -0
- package/dist/templates/shared-docs/claude-agent/intro.md +3 -0
- package/dist/templates/shared-docs/readme/maintaining-this-harness.md +14 -0
- package/dist/templates/shared-docs/readme/run-history.md +5 -0
- package/dist/templates/shared-docs/readme/session-lifecycle.md +20 -0
- package/package.json +1 -1
- package/dist/templates/har-boilerplate/CLAUDE.agent.md +0 -83
- package/dist/templates/har-boilerplate/README.md +0 -199
- package/dist/templates/har-boilerplate/STAGES.md +0 -99
- package/dist/templates/har-boilerplate/agent-cli.sh +0 -225
- package/dist/templates/har-boilerplate/agent-slot.sh +0 -850
- package/dist/templates/har-boilerplate/attach.sh +0 -22
- package/dist/templates/har-boilerplate/ecosystem.agent.template.cjs +0 -52
- package/dist/templates/har-boilerplate/env.template +0 -40
- package/dist/templates/har-boilerplate/launch.sh +0 -323
- package/dist/templates/har-boilerplate/preflight.sh +0 -32
- package/dist/templates/har-boilerplate/provision-toolchain.sh +0 -766
- package/dist/templates/har-boilerplate/setup-infra.sh +0 -178
- package/dist/templates/har-boilerplate/stages/README.sh +0 -11
- package/dist/templates/har-boilerplate/teardown.sh +0 -87
- package/dist/templates/har-boilerplate/verify.sh +0 -249
- package/dist/templates/har-boilerplate-cli/README.md +0 -157
- package/dist/templates/har-boilerplate-cli/STAGES.md +0 -99
- package/dist/templates/har-boilerplate-cli/agent-cli.sh +0 -160
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +0 -832
- package/dist/templates/har-boilerplate-cli/launch.sh +0 -216
- package/dist/templates/har-boilerplate-cli/preflight.sh +0 -28
- package/dist/templates/har-boilerplate-cli/provision-toolchain.sh +0 -766
- package/dist/templates/har-boilerplate-cli/setup-infra.sh +0 -178
- package/dist/templates/har-boilerplate-cli/stages/README.sh +0 -11
- package/dist/templates/har-boilerplate-cli/teardown.sh +0 -66
- package/dist/templates/har-boilerplate-cli/verify.sh +0 -210
- package/dist/templates/har-boilerplate-ios/ADAPT-PROMPT.md +0 -63
- package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +0 -90
- package/dist/templates/har-boilerplate-ios/README.md +0 -173
- package/dist/templates/har-boilerplate-ios/STAGES.md +0 -99
- package/dist/templates/har-boilerplate-ios/agent-cli.sh +0 -149
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +0 -479
- package/dist/templates/har-boilerplate-ios/launch.sh +0 -206
- package/dist/templates/har-boilerplate-ios/provision-toolchain.sh +0 -766
- package/dist/templates/har-boilerplate-ios/setup-infra.sh +0 -112
- package/dist/templates/har-boilerplate-ios/simulator.sh +0 -492
- package/dist/templates/har-boilerplate-ios/stages/README.sh +0 -11
- package/dist/templates/har-boilerplate-ios/teardown.sh +0 -68
- package/dist/templates/har-boilerplate-ios/verify.sh +0 -185
- package/dist/templates/runtime-bundles/pm2-runtime/README.md +0 -4
- package/dist/templates/runtime-bundles/shared-kernel/README.md +0 -3
- package/dist/templates/runtime-bundles/shared-kernel/provision-toolchain.sh +0 -766
- package/dist/templates/runtime-bundles/xcode-sim/README.md +0 -4
- package/dist/templates/runtime-bundles/xcode-sim/simulator.sh +0 -492
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
# .har — Agent Harness
|
|
2
|
-
|
|
3
|
-
This directory is the **agent harness** for this repository. It lets AI coding agents (Cursor, Claude Code, etc.) run the project in isolated environments with their own ports, database, and verification workflow.
|
|
4
|
-
|
|
5
|
-
Generated and maintained by [`har`](https://github.com/os-factory/har). Run `har env maintain` when the repo stack changes.
|
|
6
|
-
|
|
7
|
-
**The harness is how you run this project.** Need the app live — manual testing, a browser session, screenshots? `launch` a slot; don't hand-roll docker/dev-server startup. 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, checksums) — do not edit |
|
|
15
|
-
| `harness.env` | Shared config: primary app, ports, agent slot limits, `HARNESS_INFRA_SERVICES`, toolchain provisioning, 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 shared Docker infra + create template database |
|
|
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>` |
|
|
24
|
-
| `verify.sh` | Verification pipeline (smoke by default; --full adds tests, lint, e2e) |
|
|
25
|
-
| `teardown.sh` | Tear down one agent slot |
|
|
26
|
-
| `agent-cli.sh` | Manage a running agent (status, logs, psql, health) |
|
|
27
|
-
| `attach.sh` | Attach to agent tmux session |
|
|
28
|
-
| `env.template` | Per-agent env vars (expanded by `launch.sh`) |
|
|
29
|
-
| `ecosystem.agent.template.cjs` | PM2 processes for the **primary app only** (expanded by `launch.sh`) |
|
|
30
|
-
| `ecosystem.shared.config.cjs` | Optional — shared app services started once by `setup-infra.sh` (only when the repo has supporting services) |
|
|
31
|
-
| `docker-compose.agent.yml` | Shared infrastructure containers |
|
|
32
|
-
| `CLAUDE.agent.md` | Detailed instructions for coding agents |
|
|
33
|
-
| `justfile` | Optional shortcuts (requires `just`) |
|
|
34
|
-
|
|
35
|
-
## Quick start
|
|
36
|
-
|
|
37
|
-
**Preferred — har CLI or MCP** (persists run history under `.har/runs/`):
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
har env launch 1
|
|
41
|
-
har env verify 1
|
|
42
|
-
har env verify 1 --full
|
|
43
|
-
har env teardown 1
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
In Cursor with HAR MCP configured: use `har_launch_environment`, `har_run_verification`, and `har_teardown_environment`.
|
|
47
|
-
|
|
48
|
-
**Shell fallback** (no CLI/MCP installed):
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
./.har/setup-infra.sh # when HARNESS_INFRA_SERVICES is non-empty
|
|
52
|
-
./.har/launch.sh 1
|
|
53
|
-
./.har/verify.sh 1 # quick: ecosystem smoke + health
|
|
54
|
-
./.har/verify.sh 1 --full # + conventional tests/lint, browser-e2e (if installed)
|
|
55
|
-
./.har/teardown.sh 1
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Read **`stages.json`** for registered stages and **`verificationStages`** for the expected pass set.
|
|
59
|
-
|
|
60
|
-
## Verification contract
|
|
61
|
-
|
|
62
|
-
Steps in `verify.sh` are **project-specific examples** — adapt them to your stack
|
|
63
|
-
during `har env init` / `har env maintain`. The table describes each tier's intent,
|
|
64
|
-
not a fixed command list.
|
|
65
|
-
|
|
66
|
-
| Mode | Command | Typical steps |
|
|
67
|
-
|------|---------|---------------|
|
|
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.
|
|
75
|
-
|
|
76
|
-
Install Playwright plugin: `har env add-plugin playwright` (optional). UI changes should add or update specs under `tests/`.
|
|
77
|
-
|
|
78
|
-
## Readiness layers
|
|
79
|
-
|
|
80
|
-
Do not treat a health endpoint as the whole definition of success. Adapt this
|
|
81
|
-
section for the repository:
|
|
82
|
-
|
|
83
|
-
| Layer | What it means | Where to encode it |
|
|
84
|
-
|-------|---------------|--------------------|
|
|
85
|
-
| Infra ready | Shared services and template data stores exist | `setup-infra.sh`, `docker-compose.agent.yml` |
|
|
86
|
-
| Slot data ready | Every per-slot data store is created/cloned | `launch.sh` |
|
|
87
|
-
| Process ready | Primary app is running and health passes | `launch.sh`, `verify.sh` |
|
|
88
|
-
| Agent usable | Login/API/UI smoke works with documented data | `HARNESS_READINESS_CMD`, browser-e2e, `CLAUDE.agent.md` |
|
|
89
|
-
|
|
90
|
-
If full local-dev setup is too heavy to run in the harness, document the skipped
|
|
91
|
-
steps and add the minimum substitute directly in `.har/` scripts (for example an
|
|
92
|
-
idempotent bootstrap for required users/tenants/settings). Health alone is not
|
|
93
|
-
enough for UI/auth apps.
|
|
94
|
-
|
|
95
|
-
## Run history
|
|
96
|
-
|
|
97
|
-
| Entry point | Writes `.har/runs/`? |
|
|
98
|
-
|-------------|------------------------|
|
|
99
|
-
| `./.har/*.sh` | No — same scripts, no run record |
|
|
100
|
-
| `har env …` / MCP | Yes — under main checkout `.har/runs/YYYY-MM-DD/` |
|
|
101
|
-
|
|
102
|
-
With git worktree slots, verification runs code in the worktree but run JSON stays in the main repo `.har/runs/`. Each record includes `workDir` when a slot is active.
|
|
103
|
-
|
|
104
|
-
## For coding agents
|
|
105
|
-
|
|
106
|
-
1. Read repo [`AGENTS.md`](../AGENTS.md)
|
|
107
|
-
2. Read this file and `stages.json`
|
|
108
|
-
3. After launch, read `.har/CLAUDE.agent.md` for slot URLs and definition of done
|
|
109
|
-
|
|
110
|
-
Prefer HAR MCP tools or `har env …` for launch, verify, and teardown. Use `./.har/*.sh` only when the CLI is not installed.
|
|
111
|
-
|
|
112
|
-
Always use `./.har/agent-cli.sh <id> ...` — never hardcoded ports.
|
|
113
|
-
|
|
114
|
-
## Architecture
|
|
115
|
-
|
|
116
|
-
Each agent slot gets isolated app ports. Defaults follow `BASE + (AGENT_ID × HARNESS_PORT_STEP)`; when a default is busy, `launch.sh` scans the slot lane (`STEP` increments) and writes the resolved ports to `.env.agent.<id>` and `.har/slots/agent-<id>.json`.
|
|
117
|
-
|
|
118
|
-
Configure how many slots your machine can run in parallel in `.har/stages.json` (`agentSlots`). Bash scripts and the CLI read that first; `harness.env` keeps legacy `HARNESS_AGENT_SLOT_*` exports in sync via `har env maintain --finalize`.
|
|
119
|
-
|
|
120
|
-
| Service | Agent 1 (default) | Agent 2 (default) |
|
|
121
|
-
|---------|-------------------|-------------------|
|
|
122
|
-
| Frontend | 3010 | 3020 |
|
|
123
|
-
| API | 8010 | 8020 |
|
|
124
|
-
| Node debug | 9210 | 9220 |
|
|
125
|
-
|
|
126
|
-
## Port & shared services
|
|
127
|
-
|
|
128
|
-
### Port allocation
|
|
129
|
-
|
|
130
|
-
| Layer | Scope | Rule | On conflict |
|
|
131
|
-
|-------|-------|------|-------------|
|
|
132
|
-
| App — frontend | Per slot | `HARNESS_FE_BASE_PORT + (AGENT_ID × HARNESS_PORT_STEP)` | Scan `STEP` increments within the slot lane |
|
|
133
|
-
| App — API | Per slot | `HARNESS_API_BASE_PORT + (AGENT_ID × STEP)` | Same scan policy |
|
|
134
|
-
| Node debug | Per slot | `9200 + (AGENT_ID × STEP)` | Same scan policy |
|
|
135
|
-
| Shared Postgres | Per machine | `HARNESS_DB_PORT_DEFAULT` | Scan `HARNESS_DB_PORT_SCAN_START..END` |
|
|
136
|
-
| MinIO / S3 | Per machine | `HARNESS_MINIO_PORT_DEFAULT` (+ console port) | Scan configured ranges in `harness.env` |
|
|
137
|
-
| Mailpit | Per machine | `HARNESS_MAILPIT_*_PORT_DEFAULT` | Scan configured ranges |
|
|
138
|
-
| Headless browser | Per machine | `HARNESS_BROWSER_PORT_DEFAULT` | Scan configured ranges |
|
|
139
|
-
|
|
140
|
-
Resolved ports may differ from the formula when something else is already bound. Always use `./.har/agent-cli.sh <id>` or read `.har/slots/agent-<id>.json` — never hardcode `3010`, `15432`, etc. in app code or tests.
|
|
141
|
-
|
|
142
|
-
### Shared vs per-slot
|
|
143
|
-
|
|
144
|
-
| Resource | Model | Configuration |
|
|
145
|
-
|----------|-------|---------------|
|
|
146
|
-
| Postgres | One shared container; per-slot database `agent_<id>` cloned from template | `HARNESS_INFRA_SERVICES="db"` |
|
|
147
|
-
| MinIO / S3 | One shared container; per-slot bucket `agent-<id>` | `HARNESS_INFRA_SERVICES="... minio"` |
|
|
148
|
-
| Mailpit, Redis, etc. | One shared container on a scanned host port | Listed in `HARNESS_INFRA_SERVICES` |
|
|
149
|
-
| Primary application | One PM2 ecosystem per slot (isolated ports) | `HARNESS_PRIMARY_APP`, `ecosystem.agent.template.cjs` |
|
|
150
|
-
| Internal supporting services | Shared across all slots | `docker-compose.agent.yml` or `ecosystem.shared.config.cjs` |
|
|
151
|
-
|
|
152
|
-
Shared infra starts once via `./.har/setup-infra.sh` (also run automatically by `launch.sh`). Per-slot databases are cloned in `launch.sh`.
|
|
153
|
-
|
|
154
|
-
### Do not
|
|
155
|
-
|
|
156
|
-
- Hardcode default ports (`3000`, `15432`, `3847`, …) in application code, tests, or agent docs — read from `.env.agent.<id>`, `agent-cli.sh`, or the slot registry
|
|
157
|
-
- Run raw `docker compose` for harness infrastructure — use `setup-infra.sh` / `launch.sh` so ports are scanned and persisted in `.har/state/infra.env`
|
|
158
|
-
|
|
159
|
-
### Primary app vs shared services
|
|
160
|
-
|
|
161
|
-
Each slot runs **only the primary application** (`HARNESS_PRIMARY_APP` in `harness.env`) — the app agents modify. Everything else runs **once**, shared by all slots on fixed ports:
|
|
162
|
-
|
|
163
|
-
- **External dependencies** (Postgres, Redis, mail, ...): services in `docker-compose.agent.yml`, enabled via the `HARNESS_INFRA_SERVICES` list in `harness.env`, started by `setup-infra.sh`.
|
|
164
|
-
- **Internal supporting services** (other services of a monolith/monorepo the agent depends on but does not change): either compose services in `docker-compose.agent.yml`, or PM2 processes in an optional `.har/ecosystem.shared.config.cjs` (named `har-shared-<name>`, started by `setup-infra.sh`).
|
|
165
|
-
|
|
166
|
-
Isolation still applies where it matters: each slot gets its own database (`agent_<id>`, cloned from the template DB), ports, and git worktree.
|
|
167
|
-
|
|
168
|
-
## Maintaining this harness
|
|
169
|
-
|
|
170
|
-
When the project stack changes (new services, different test commands, new env vars):
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
har env maintain
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
The authoring agent updates scripts and this README. Review changes before committing.
|
|
177
|
-
|
|
178
|
-
**Do not** put runtime behavior in YAML — edit the scripts directly.
|
|
179
|
-
|
|
180
|
-
## Session lifecycle
|
|
181
|
-
|
|
182
|
-
Every `launch` starts a **fresh session**: a new git worktree from the **main
|
|
183
|
-
checkout's current HEAD** at
|
|
184
|
-
`~/worktrees/<base-branch>-<sha4>-har-agent-<id>-<rand4>`, on a branch of the same name.
|
|
185
|
-
Switch that checkout to your intended base before launch. The session is recorded in
|
|
186
|
-
`.har/slots/agent-<id>.json` (the slot registry) — status, verify, and teardown resolve
|
|
187
|
-
the work dir through it. Make ALL file edits under the work dir printed by launch,
|
|
188
|
-
never in the main checkout.
|
|
189
|
-
|
|
190
|
-
- Occupied slots always block a new launch: `har env complete <id>` (or `teardown <id>`),
|
|
191
|
-
then `har env launch <id>`. A new launch never chooses `main` for you — switch the
|
|
192
|
-
main checkout to your intended base first.
|
|
193
|
-
- `teardown` removes the worktree but **keeps the session branch** so you can push it
|
|
194
|
-
or open a PR (`--delete-branch` to drop it).
|
|
195
|
-
- If launch fails after creating a worktree/env file, the registry records `status: failed`.
|
|
196
|
-
Resume it instead of starting fresh: `har env launch <id> --resume` or `har env recover <id>`.
|
|
197
|
-
- `har env complete <id>` finishes a session: full verify (recorded as a validation),
|
|
198
|
-
then teardown — branch kept.
|
|
199
|
-
- `--no-worktree` runs the slot from the repo root instead (single-agent mode).
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# HAR Stages — authoring guide
|
|
2
|
-
|
|
3
|
-
Stages are the harness's single vocabulary for runnable checks and lifecycle
|
|
4
|
-
actions. Everything — shipped plugins (`playwright`, `rocketsim`, `kerno`, `gitleaks`, `trivy`, `semgrep`), your
|
|
5
|
-
project's test/lint commands, bespoke validation scripts — registers in
|
|
6
|
-
`.har/stages.json` with the same schema, and agents interact with stages only
|
|
7
|
-
through that registry (CLI `har env verify`, MCP `har_run_stage` /
|
|
8
|
-
`har_run_verification`), never through stack-specific tooling.
|
|
9
|
-
|
|
10
|
-
Philosophy: *plugins install stages; agents only talk to the stage registry.*
|
|
11
|
-
|
|
12
|
-
## The registry: `.har/stages.json`
|
|
13
|
-
|
|
14
|
-
```jsonc
|
|
15
|
-
{
|
|
16
|
-
"verificationStages": ["typecheck", "unit-tests", "api-health", "browser-e2e"],
|
|
17
|
-
"stages": [
|
|
18
|
-
{
|
|
19
|
-
"id": "browser-e2e", // stable, shell-friendly slug
|
|
20
|
-
"kind": "test", // setup | launch | verify | test | inspect | reset | teardown | custom
|
|
21
|
-
"description": "Playwright E2E",
|
|
22
|
-
"script": "stages/browser-e2e.sh", // relative to .har/ — OR use "command"
|
|
23
|
-
"requiresAgentId": true, // default true for test/verify/custom kinds
|
|
24
|
-
"artifacts": [{ "path": ".har/artifacts/browser-e2e", "kind": "directory" }]
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"id": "unit-tests-fast",
|
|
28
|
-
"kind": "test",
|
|
29
|
-
"command": "npm test -- --agent {agentId}" // {agentId} is substituted at run time
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Optional stage fields: `cwd` (working directory), `env` (extra env vars),
|
|
36
|
-
`group`, `acceptsArgs` (extra CLI args the stage accepts, e.g. `["--full"]`).
|
|
37
|
-
|
|
38
|
-
## Two ways to define a stage
|
|
39
|
-
|
|
40
|
-
**Command stages** — the default for simple checks (`npm test`, `swiftlint`,
|
|
41
|
-
`make check`). One JSON entry, zero files:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
har env add-stage unit-tests-fast --custom --kind test --command "npm test" --verification
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**Script stages** — for anything that needs the slot's env, ports, or
|
|
48
|
-
artifacts. Scaffold a contract-compliant skeleton:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
har env add-stage db-integrity --custom --script --description "Check DB invariants"
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
then implement the TODO block in `.har/stages/db-integrity.sh`.
|
|
55
|
-
|
|
56
|
-
## The stage script contract
|
|
57
|
-
|
|
58
|
-
Every script under `.har/stages/` must:
|
|
59
|
-
|
|
60
|
-
1. Source `harness.env` and `agent-slot.sh` from `.har/`. Before sourcing,
|
|
61
|
-
set `SCRIPT_DIR` to the `.har/` directory (not `stages/`) — `agent-slot.sh`
|
|
62
|
-
resolves the slot registry via `$SCRIPT_DIR/slots/...`.
|
|
63
|
-
2. Take the agent slot id as `$1` (validate with `validate_agent_id`); extra
|
|
64
|
-
args may follow.
|
|
65
|
-
3. Load the slot env via `resolve_agent_env_file` and run checks from the
|
|
66
|
-
agent's work dir (`resolve_agent_work_dir`).
|
|
67
|
-
4. Write artifacts (reports, screenshots, logs) under `.har/artifacts/<id>/`.
|
|
68
|
-
5. Print **only** the normalized JSON result object on stdout
|
|
69
|
-
(`status`, `stageId`, `agent_id`, `total_ms`, …); log progress to stderr.
|
|
70
|
-
6. Exit with the real status code (0 = pass).
|
|
71
|
-
|
|
72
|
-
The scaffolded skeleton implements all of this — replace its TODO block.
|
|
73
|
-
|
|
74
|
-
## Verification membership
|
|
75
|
-
|
|
76
|
-
Listing a stage id in `verificationStages` is what includes it in
|
|
77
|
-
`har env verify <id> --full`. Ids that match a registered stage run via their
|
|
78
|
-
script/command; ids without a registry entry (e.g. `typecheck`, `api-health`)
|
|
79
|
-
are inline steps owned by `.har/verify.sh`. Lifecycle kinds
|
|
80
|
-
(`setup`/`launch`/`reset`/`teardown`/`inspect`) and `verify` itself never run
|
|
81
|
-
as part of verification, even if listed.
|
|
82
|
-
|
|
83
|
-
## Commit gate
|
|
84
|
-
|
|
85
|
-
The registry also holds the optional `commitGate` config (installed via
|
|
86
|
-
`har hooks install`): `{ "commitGate": { "mode": "block" | "warn", "scope":
|
|
87
|
-
"worktrees" | "all" } }` controls whether unverified change batches may be
|
|
88
|
-
committed.
|
|
89
|
-
|
|
90
|
-
## Shipped plugins
|
|
91
|
-
|
|
92
|
-
`har env add-plugin --list` shows available plugins; `har env add-plugin
|
|
93
|
-
playwright` (web), `har env add-plugin rocketsim` (iOS), or `har env
|
|
94
|
-
add-plugin kerno` (backend), `har env add-plugin gitleaks` (secrets scanning, any
|
|
95
|
-
stack), or `har env add-plugin trivy` (dependency + IaC security scan, any stack),
|
|
96
|
-
or `har env add-plugin semgrep` (SAST, any stack)
|
|
97
|
-
installs one. A
|
|
98
|
-
plugin is just packaging: it copies files, merges `package.json` fragments,
|
|
99
|
-
and registers stages through the exact same registry as `add-stage --custom`.
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Namespaced CLI for managing a running agent environment.
|
|
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
|
-
load_agent_ports "$AGENT_ID" "$REPO_ROOT"
|
|
21
|
-
DB_PORT="${DB_PORT:-${AGENT_DB_PORT:-${HARNESS_DB_PORT_DEFAULT:-15432}}}"
|
|
22
|
-
export PGPASSWORD="password"
|
|
23
|
-
|
|
24
|
-
PM2_SLOT_PREFIX="$(har_pm2_slot_prefix "$AGENT_ID")"
|
|
25
|
-
|
|
26
|
-
case "$COMMAND" in
|
|
27
|
-
status)
|
|
28
|
-
ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT" || true)"
|
|
29
|
-
WORKTREE_DIR="$(existing_slot_worktree "$AGENT_ID")"
|
|
30
|
-
REG_FILE="$(slot_registry_file "$AGENT_ID")"
|
|
31
|
-
PM2_RAW=$($(har_pkg_exec) pm2 jlist 2>/dev/null || true)
|
|
32
|
-
PM2_FOUND=false
|
|
33
|
-
PM2_FOREIGN=false
|
|
34
|
-
PM2_LEGACY=false
|
|
35
|
-
|
|
36
|
-
if [ -n "$PM2_RAW" ]; then
|
|
37
|
-
set +e
|
|
38
|
-
echo "$PM2_RAW" | node -e "
|
|
39
|
-
const agentId = '${AGENT_ID}';
|
|
40
|
-
const project = '${HARNESS_PROJECT_NAME}';
|
|
41
|
-
const slotPrefix = 'har-' + project + '-agent-' + agentId + '-';
|
|
42
|
-
const legacyPrefix = 'agent-' + agentId + '-';
|
|
43
|
-
let raw = '';
|
|
44
|
-
process.stdin.on('data', c => raw += c);
|
|
45
|
-
process.stdin.on('end', () => {
|
|
46
|
-
try {
|
|
47
|
-
const arr = JSON.parse(raw);
|
|
48
|
-
if (!Array.isArray(arr)) process.exit(1);
|
|
49
|
-
const owned = arr.filter(x => x.name && x.name.startsWith(slotPrefix));
|
|
50
|
-
const foreignProject = arr.filter(x =>
|
|
51
|
-
x.name && x.name.startsWith('har-') && x.name.includes('-agent-' + agentId + '-') && !x.name.startsWith(slotPrefix));
|
|
52
|
-
const legacy = arr.filter(x => x.name && x.name.startsWith(legacyPrefix) && !x.name.startsWith('har-'));
|
|
53
|
-
if (owned.length > 0) {
|
|
54
|
-
console.log('Agent ' + agentId + ' processes (' + project + '):');
|
|
55
|
-
owned.forEach(p => {
|
|
56
|
-
const s = (p.pm2_env?.status || 'unknown').padEnd(10);
|
|
57
|
-
const mem = Math.round((p.monit?.memory || 0) / 1024 / 1024) + 'MB';
|
|
58
|
-
const cpu = (p.monit?.cpu || 0) + '%';
|
|
59
|
-
const cwd = p.pm2_env?.pm_cwd || p.pm2_env?.cwd || '';
|
|
60
|
-
console.log(' ' + p.name.padEnd(48) + s + ' mem=' + mem + ' cpu=' + cpu);
|
|
61
|
-
if (cwd) console.log(' cwd: ' + cwd);
|
|
62
|
-
});
|
|
63
|
-
process.exit(0);
|
|
64
|
-
}
|
|
65
|
-
if (foreignProject.length > 0 || legacy.length > 0) {
|
|
66
|
-
if (foreignProject.length > 0) {
|
|
67
|
-
console.log('ERROR: foreign PM2 processes match agent ' + agentId + ' but belong to another harness:');
|
|
68
|
-
foreignProject.forEach(p => console.log(' ' + p.name + ' cwd=' + (p.pm2_env?.pm_cwd || p.pm2_env?.cwd || 'unknown')));
|
|
69
|
-
}
|
|
70
|
-
if (legacy.length > 0) {
|
|
71
|
-
console.log('ERROR: legacy PM2 processes (pre project-scoping) match agent ' + agentId + ':');
|
|
72
|
-
legacy.forEach(p => console.log(' ' + p.name + ' cwd=' + (p.pm2_env?.pm_cwd || p.pm2_env?.cwd || 'unknown')));
|
|
73
|
-
}
|
|
74
|
-
process.exit(2);
|
|
75
|
-
}
|
|
76
|
-
process.exit(1);
|
|
77
|
-
} catch {
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
"
|
|
82
|
-
pm2_status=$?
|
|
83
|
-
set -e
|
|
84
|
-
case "$pm2_status" in
|
|
85
|
-
0) PM2_FOUND=true ;;
|
|
86
|
-
2) PM2_FOREIGN=true ;;
|
|
87
|
-
esac
|
|
88
|
-
fi
|
|
89
|
-
|
|
90
|
-
if [ "$PM2_FOREIGN" = true ]; then
|
|
91
|
-
exit 1
|
|
92
|
-
fi
|
|
93
|
-
|
|
94
|
-
if [ "$PM2_FOUND" = true ]; then
|
|
95
|
-
if [ ! -f "$REG_FILE" ]; then
|
|
96
|
-
echo "ERROR: slot registry missing at $REG_FILE but PM2 processes exist for this project." >&2
|
|
97
|
-
echo " Another harness may have been torn down incorrectly, or this slot was started outside launch.sh." >&2
|
|
98
|
-
exit 1
|
|
99
|
-
fi
|
|
100
|
-
REG_PROJECT="$(read_slot_field "$REG_FILE" projectName || true)"
|
|
101
|
-
if [ -n "$REG_PROJECT" ] && [ "$REG_PROJECT" != "$HARNESS_PROJECT_NAME" ]; then
|
|
102
|
-
echo "ERROR: slot registry projectName=${REG_PROJECT} does not match harness ${HARNESS_PROJECT_NAME}." >&2
|
|
103
|
-
exit 1
|
|
104
|
-
fi
|
|
105
|
-
if [ -z "$ENV_FILE" ]; then
|
|
106
|
-
echo "ERROR: .env.agent.${AGENT_ID} could not be resolved — teardown this slot, then relaunch." >&2
|
|
107
|
-
exit 1
|
|
108
|
-
fi
|
|
109
|
-
elif [ -n "$ENV_FILE" ] && [ -f "$ENV_FILE" ]; then
|
|
110
|
-
echo "Agent ${AGENT_ID}: active (no PM2 processes)"
|
|
111
|
-
# shellcheck source=/dev/null
|
|
112
|
-
source "$ENV_FILE" 2>/dev/null || true
|
|
113
|
-
echo " Work dir: $(resolve_agent_work_dir "$ENV_FILE" "$AGENT_ID")"
|
|
114
|
-
[ -n "$WORKTREE_DIR" ] && [ -d "$WORKTREE_DIR" ] && echo " Worktree: $WORKTREE_DIR"
|
|
115
|
-
echo " Frontend: http://localhost:${FE_PORT}"
|
|
116
|
-
echo " API: http://localhost:${API_PORT}"
|
|
117
|
-
else
|
|
118
|
-
echo "No active environment for agent ${AGENT_ID}"
|
|
119
|
-
har_suggest_launch "$AGENT_ID"
|
|
120
|
-
fi
|
|
121
|
-
;;
|
|
122
|
-
|
|
123
|
-
logs)
|
|
124
|
-
SERVICE="${3:-}"
|
|
125
|
-
if [ -n "$SERVICE" ]; then
|
|
126
|
-
$(har_pkg_exec) pm2 logs "${PM2_SLOT_PREFIX}-${SERVICE}" --lines 100
|
|
127
|
-
else
|
|
128
|
-
$(har_pkg_exec) pm2 logs --name "${PM2_SLOT_PREFIX}" --lines 100
|
|
129
|
-
fi
|
|
130
|
-
;;
|
|
131
|
-
|
|
132
|
-
restart)
|
|
133
|
-
SERVICE="${3:-}"
|
|
134
|
-
PKG_EXEC="$(har_pkg_exec)"
|
|
135
|
-
if [ -n "$SERVICE" ]; then
|
|
136
|
-
$(har_pkg_exec) pm2 restart "${PM2_SLOT_PREFIX}-${SERVICE}"
|
|
137
|
-
else
|
|
138
|
-
$(har_pkg_exec) pm2 jlist 2>/dev/null | node -e "
|
|
139
|
-
const prefix = '${PM2_SLOT_PREFIX}-';
|
|
140
|
-
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
|
|
141
|
-
const names = d.filter(x => x.name && x.name.startsWith(prefix)).map(x => x.name);
|
|
142
|
-
names.forEach(n => require('child_process').execSync('${PKG_EXEC} pm2 restart ' + n, {stdio:'inherit'}));
|
|
143
|
-
if (names.length === 0) console.log('No processes found for ${PM2_SLOT_PREFIX}');
|
|
144
|
-
" 2>/dev/null || true
|
|
145
|
-
fi
|
|
146
|
-
;;
|
|
147
|
-
|
|
148
|
-
psql)
|
|
149
|
-
QUERY="${3:-}"
|
|
150
|
-
if [ -n "$QUERY" ]; then
|
|
151
|
-
har_pg psql -d "agent_${AGENT_ID}" -c "$QUERY"
|
|
152
|
-
else
|
|
153
|
-
har_pg psql -d "agent_${AGENT_ID}"
|
|
154
|
-
fi
|
|
155
|
-
;;
|
|
156
|
-
|
|
157
|
-
health)
|
|
158
|
-
if [ -n "${HARNESS_HEALTH_CHECK_PATH:-}" ]; then
|
|
159
|
-
curl -sf "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}" | node -e "
|
|
160
|
-
const d = require('fs').readFileSync('/dev/stdin','utf8');
|
|
161
|
-
try { console.log(JSON.stringify(JSON.parse(d), null, 2)); } catch { console.log(d); }
|
|
162
|
-
" || curl -v "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}"
|
|
163
|
-
else
|
|
164
|
-
echo "No health check path configured in harness.env"
|
|
165
|
-
fi
|
|
166
|
-
;;
|
|
167
|
-
|
|
168
|
-
url)
|
|
169
|
-
echo "Frontend: http://localhost:${FE_PORT}"
|
|
170
|
-
echo "API: http://localhost:${API_PORT}"
|
|
171
|
-
har_infra_enabled db && echo "Database: agent_${AGENT_ID} @ localhost:${DB_PORT}"
|
|
172
|
-
har_infra_enabled minio && echo "MinIO: http://localhost:${AGENT_MINIO_CONSOLE_PORT:-19001}"
|
|
173
|
-
har_infra_enabled headless-browser && echo "Browser: http://localhost:${AGENT_BROWSER_PORT:-13001}"
|
|
174
|
-
har_infra_enabled mailpit && echo "Mailpit: http://localhost:${AGENT_MAILPIT_WEB_PORT:-18025}"
|
|
175
|
-
;;
|
|
176
|
-
|
|
177
|
-
reset-db)
|
|
178
|
-
echo "==> Resetting database for agent ${AGENT_ID}..."
|
|
179
|
-
har_pg psql -d postgres -c \
|
|
180
|
-
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='agent_${AGENT_ID}';" \
|
|
181
|
-
>/dev/null
|
|
182
|
-
har_pg dropdb --if-exists "agent_${AGENT_ID}"
|
|
183
|
-
har_pg createdb -T "$HARNESS_TEMPLATE_DB" "agent_${AGENT_ID}"
|
|
184
|
-
echo "✓ Database reset to clean state"
|
|
185
|
-
;;
|
|
186
|
-
|
|
187
|
-
slow-queries)
|
|
188
|
-
har_pg psql -d "agent_${AGENT_ID}" -c "
|
|
189
|
-
SELECT round(mean_exec_time::numeric, 2) AS mean_ms,
|
|
190
|
-
calls,
|
|
191
|
-
left(query, 120) AS query
|
|
192
|
-
FROM pg_stat_statements
|
|
193
|
-
ORDER BY mean_exec_time DESC
|
|
194
|
-
LIMIT 20;" 2>/dev/null || echo "pg_stat_statements extension not available"
|
|
195
|
-
;;
|
|
196
|
-
|
|
197
|
-
exec)
|
|
198
|
-
shift 2
|
|
199
|
-
if [ $# -eq 0 ]; then
|
|
200
|
-
echo "Usage: agent-cli.sh ${AGENT_ID} exec <command>" >&2
|
|
201
|
-
exit 1
|
|
202
|
-
fi
|
|
203
|
-
WORK_DIR="$(resolve_agent_work_dir "" "$AGENT_ID" 2>/dev/null || true)"
|
|
204
|
-
[ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ] || WORK_DIR="$REPO_ROOT"
|
|
205
|
-
PGHOST=localhost PGPORT="$DB_PORT" PGUSER=postgres PGDATABASE="agent_${AGENT_ID}" \
|
|
206
|
-
bash -c "cd '$WORK_DIR' && $*"
|
|
207
|
-
;;
|
|
208
|
-
|
|
209
|
-
attach)
|
|
210
|
-
SESSION="$(har_tmux_session "$AGENT_ID")"
|
|
211
|
-
if ! tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
212
|
-
echo "No tmux session found: $SESSION" >&2
|
|
213
|
-
exit 1
|
|
214
|
-
fi
|
|
215
|
-
tmux attach -t "$SESSION"
|
|
216
|
-
;;
|
|
217
|
-
|
|
218
|
-
*)
|
|
219
|
-
echo "Unknown command: $COMMAND" >&2
|
|
220
|
-
echo ""
|
|
221
|
-
echo "Commands: status, logs [service], restart [service], psql [query],"
|
|
222
|
-
echo " health, url, reset-db, slow-queries, exec <cmd>, attach"
|
|
223
|
-
exit 1
|
|
224
|
-
;;
|
|
225
|
-
esac
|