@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,61 @@
1
+ {
2
+ "version": "1",
3
+ "agentSlots": {
4
+ "min": 1,
5
+ "max": 3
6
+ },
7
+ "stages": [
8
+ {
9
+ "id": "setup-infra",
10
+ "kind": "setup",
11
+ "description": "Optional shared Docker infrastructure",
12
+ "command": "./.har/setup-infra.sh"
13
+ },
14
+ {
15
+ "id": "launch",
16
+ "kind": "launch",
17
+ "description": "Prepare agent slot (git worktree, deps, env file)",
18
+ "command": "./.har/launch.sh {agentId}",
19
+ "artifacts": [
20
+ {
21
+ "path": ".env.agent.{agentId}",
22
+ "kind": "file",
23
+ "description": "Generated per-agent environment file"
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "id": "verify",
29
+ "kind": "verify",
30
+ "description": "Verification pipeline (quick by default; --full adds lint, build, browser-e2e when installed)",
31
+ "command": "./.har/verify.sh {agentId}",
32
+ "acceptsArgs": ["--full"],
33
+ "artifacts": [
34
+ {
35
+ "path": ".har/artifacts",
36
+ "kind": "directory",
37
+ "description": "Optional verification outputs"
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "id": "status",
43
+ "kind": "inspect",
44
+ "description": "Inspect agent slot status and worktree path",
45
+ "command": "./.har/agent-cli.sh {agentId} status",
46
+ "artifacts": [
47
+ {
48
+ "path": ".har/logs",
49
+ "kind": "directory",
50
+ "description": "Optional harness logs"
51
+ }
52
+ ]
53
+ },
54
+ {
55
+ "id": "teardown",
56
+ "kind": "teardown",
57
+ "description": "Remove worktree and env file for the slot",
58
+ "command": "./.har/teardown.sh {agentId}"
59
+ }
60
+ ]
61
+ }
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env bash
2
+ # Tear down an agent slot for CLI/library repos.
3
+ # The session's git branch is KEPT by default so you can push it / open a PR —
4
+ # pass --delete-branch to remove it too.
5
+ #
6
+ # Usage: ./.har/teardown.sh <agent-id> [--delete-branch]
7
+ set -euo pipefail
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11
+
12
+ # shellcheck source=/dev/null
13
+ source "$SCRIPT_DIR/harness.env"
14
+ # shellcheck source=/dev/null
15
+ source "$SCRIPT_DIR/agent-slot.sh"
16
+
17
+ AGENT_ID="${1:?Usage: teardown.sh <agent-id> [--delete-branch]}"
18
+ DELETE_BRANCH=false
19
+
20
+ for arg in "${@:2}"; do
21
+ case "$arg" in
22
+ --delete-branch) DELETE_BRANCH=true ;;
23
+ esac
24
+ done
25
+
26
+ validate_agent_id "$AGENT_ID"
27
+
28
+ echo "==> Tearing down agent ${AGENT_ID}..."
29
+
30
+ # Resolve the session from the registry; fall back to the legacy fixed path so
31
+ # pre-registry sessions stay removable.
32
+ REGISTRY_FILE="$(slot_registry_file "$AGENT_ID")"
33
+ WORKTREE_PATH=""
34
+ WORK_DIR=""
35
+ BRANCH=""
36
+ if [ -f "$REGISTRY_FILE" ]; then
37
+ WORKTREE_PATH="$(read_slot_field "$REGISTRY_FILE" worktreePath || true)"
38
+ WORK_DIR="$(read_slot_field "$REGISTRY_FILE" workDir || true)"
39
+ BRANCH="$(read_slot_field "$REGISTRY_FILE" branch || true)"
40
+ fi
41
+ [ -n "$WORKTREE_PATH" ] || WORKTREE_PATH="$HOME/worktrees/${HARNESS_PROJECT_NAME}-agent-${AGENT_ID}"
42
+
43
+ rm -f "$REPO_ROOT/.env.agent.${AGENT_ID}"
44
+ if [ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ]; then
45
+ rm -f "$WORK_DIR/.env.agent.${AGENT_ID}"
46
+ fi
47
+
48
+ if [ -d "$WORKTREE_PATH" ]; then
49
+ rm -f "$WORKTREE_PATH/.env.agent.${AGENT_ID}"
50
+ git -C "$REPO_ROOT" worktree remove "$WORKTREE_PATH" --force 2>/dev/null || rm -rf "$WORKTREE_PATH"
51
+ echo "✓ Removed worktree: $WORKTREE_PATH"
52
+ fi
53
+ git -C "$REPO_ROOT" worktree prune 2>/dev/null || true
54
+
55
+ if [ -n "$BRANCH" ]; then
56
+ if [ "$DELETE_BRANCH" = true ]; then
57
+ git -C "$REPO_ROOT" branch -D "$BRANCH" 2>/dev/null || true
58
+ echo "✓ Deleted branch: $BRANCH"
59
+ else
60
+ echo "✓ Kept branch: $BRANCH (push it or delete with: git branch -D $BRANCH)"
61
+ fi
62
+ fi
63
+
64
+ remove_slot_registry "$AGENT_ID"
65
+
66
+ echo "✓ Agent ${AGENT_ID} torn down"
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # Verification pipeline for CLI/library repos — typecheck, test, lint, build.
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
+ ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
26
+ echo "No .env.agent.${AGENT_ID} found. Run: ./.har/launch.sh ${AGENT_ID}" >&2
27
+ exit 1
28
+ }
29
+
30
+ set -a
31
+ # shellcheck source=/dev/null
32
+ source "$ENV_FILE"
33
+ set +a
34
+
35
+ WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
36
+
37
+ echo "==> Verifying agent ${AGENT_ID} in ${WORK_DIR}..." >&2
38
+
39
+ OVERALL_PASS=true
40
+ START_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
41
+ RESULTS_JSON="[]"
42
+
43
+ run_step() {
44
+ local name="$1"
45
+ local cmd="$2"
46
+ local start end elapsed exit_code output
47
+
48
+ printf " → %-40s" "$name..." >&2
49
+ start=$(date +%s%3N 2>/dev/null || echo "0")
50
+
51
+ set +e
52
+ output=$(cd "$WORK_DIR" && eval "$cmd" 2>&1)
53
+ exit_code=$?
54
+ set -e
55
+
56
+ end=$(date +%s%3N 2>/dev/null || echo "0")
57
+ elapsed=$(( end - start ))
58
+
59
+ local pass_bool step_output_escaped
60
+ if [ "$exit_code" = "0" ]; then
61
+ echo "✓ (${elapsed}ms)" >&2
62
+ pass_bool="true"
63
+ else
64
+ echo "✗ (${elapsed}ms)" >&2
65
+ echo "$output" | head -30 | sed 's/^/ /' >&2
66
+ pass_bool="false"
67
+ OVERALL_PASS=false
68
+ fi
69
+
70
+ step_output_escaped=$(echo "$output" | head -50 | \
71
+ node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(d.trim())))" \
72
+ 2>/dev/null || echo '""')
73
+
74
+ RESULTS_JSON=$(echo "$RESULTS_JSON" | node -e "
75
+ const fs = require('fs');
76
+ let arr = JSON.parse(fs.readFileSync('/dev/stdin','utf8'));
77
+ arr.push({name:'$name',pass:$pass_bool,ms:$elapsed,output:$step_output_escaped});
78
+ process.stdout.write(JSON.stringify(arr));
79
+ " 2>/dev/null || echo "$RESULTS_JSON")
80
+
81
+ if [ "$pass_bool" = "false" ] && [ -z "$FULL" ]; then
82
+ return 1
83
+ fi
84
+ }
85
+
86
+ run_step "typecheck" "npm run typecheck" || { [ -z "$FULL" ] && true; }
87
+ # Some unit tests exec dist/index.js (e.g. stage-templates CLI add-stage).
88
+ run_step "build" "npm run build" || { [ -z "$FULL" ] && true; }
89
+ run_step "unit-tests" "npm test" || { [ -z "$FULL" ] && true; }
90
+
91
+ if [ -n "$FULL" ]; then
92
+ run_step "lint" "npm run lint" || true
93
+ run_step "browser-e2e" "run_browser_e2e_if_present \"$SCRIPT_DIR\" \"$AGENT_ID\"" || true
94
+ fi
95
+
96
+ END_TOTAL=$(date +%s%3N 2>/dev/null || echo "0")
97
+ TOTAL_MS=$(( END_TOTAL - START_TOTAL ))
98
+
99
+ node -e "
100
+ const results = $RESULTS_JSON;
101
+ const overall = results.length > 0 && results.every(r => r.pass);
102
+ const out = {
103
+ status: overall ? 'pass' : 'fail',
104
+ agent_id: $AGENT_ID,
105
+ total_ms: $TOTAL_MS,
106
+ stages: results,
107
+ };
108
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
109
+ " 2>/dev/null || echo "{\"status\":\"fail\",\"agent_id\":${AGENT_ID},\"stages\":[]}"
110
+
111
+ if [ "$OVERALL_PASS" = "false" ]; then
112
+ exit 1
113
+ fi
@@ -0,0 +1,57 @@
1
+ # Adapt this iOS harness to the project
2
+
3
+ You are adapting a freshly scaffolded iOS mobile app harness. Read the existing repo
4
+ structure, then make the changes listed below. Commit when done.
5
+
6
+ ## 1 — Read before editing
7
+
8
+ - `AGENT.md` at the repo root (overview and do-nots)
9
+ - `.har/README.md` (harness index)
10
+ - `.har/harness.env` (all config vars)
11
+ - `.har/stages.json` (stage registry)
12
+ - Root of the repo: look for `*.xcworkspace`, `*.xcodeproj`, `Podfile`, `Package.swift`
13
+
14
+ ## 2 — Set Xcode project config in `.har/harness.env`
15
+
16
+ | Variable | What to set |
17
+ |----------|-------------|
18
+ | `HARNESS_XCODE_WORKSPACE` | Relative path to `.xcworkspace` (e.g. `MyApp.xcworkspace`). Use when CocoaPods or a workspace is present. Leave empty otherwise. |
19
+ | `HARNESS_XCODE_PROJECT` | Relative path to `.xcodeproj` (e.g. `MyApp.xcodeproj`). Only set when no workspace. |
20
+ | `HARNESS_XCODE_SCHEME` | Name of the shared Xcode scheme (must be marked Shared in Xcode → Product → Scheme → Manage Schemes). |
21
+ | `HARNESS_SIMULATOR_NAME` | Display name exactly as shown in `xcrun simctl list devices` (e.g. `iPhone 16`). |
22
+ | `HARNESS_BUNDLE_ID` | App bundle identifier from the Xcode target (e.g. `com.example.myapp`). |
23
+
24
+ Run `./.har/setup-infra.sh` after editing to confirm the simulator boots.
25
+
26
+ ## 3 — Verify `.har/verify.sh` builds and tests correctly
27
+
28
+ Run `./.har/launch.sh 1`, then `./.har/verify.sh 1`.
29
+
30
+ Common adaptations:
31
+ - If the project uses a custom test plan, add `-testPlan MyTests` to the `xcodebuild test` invocation.
32
+ - If SwiftLint is not used, remove or comment out the `lint` step.
33
+ - If there are UI tests that require a running app, move them to a RocketSim flow (`har env add-stage rocketsim`) rather than xcodebuild tests.
34
+
35
+ ## 4 — Add the RocketSim stage template (recommended)
36
+
37
+ ```bash
38
+ har env add-stage rocketsim
39
+ ```
40
+
41
+ Then adapt `flows/example-smoke.sh` to navigate to your app's main screen and verify it loads.
42
+ Read `.har/stages/ROCKETSIM.md` for the full authoring guide.
43
+
44
+ ## 5 — Update `AGENT.md` (repo root)
45
+
46
+ Replace the TODO section with:
47
+ - Which Xcode scheme and simulator are used
48
+ - How to run unit tests manually
49
+ - How to add a new RocketSim user flow
50
+
51
+ ## 6 — Verify everything passes
52
+
53
+ ```bash
54
+ ./.har/verify.sh 1 --full
55
+ ```
56
+
57
+ All steps should return ✓. Commit the adapted harness in the session worktree.
@@ -0,0 +1,65 @@
1
+ # Agent ${AGENT_ID} — iOS 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
+ | **Simulator** | Configured in `.har/harness.env` — `HARNESS_SIMULATOR_NAME` |
12
+ | **Scheme** | Configured in `.har/harness.env` — `HARNESS_XCODE_SCHEME` |
13
+
14
+ **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; dirty worktrees also need `--force` after user approval.
15
+
16
+ ```bash
17
+ ./.har/agent-cli.sh ${AGENT_ID} status
18
+ ```
19
+
20
+ ## Definition of done
21
+
22
+ - [ ] 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`)
23
+ - [ ] When `stages/rocketsim-flows.sh` exists, full verify includes user-flow validation — add or update flow scripts in `flows/` for UI changes
24
+ - [ ] New behavior has automated test coverage (unit tests via XCTest)
25
+ - [ ] Changes committed **in the session worktree** with a clear message
26
+ - [ ] 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
27
+
28
+ Quick loop: MCP `har_run_verification`, `har env verify ${AGENT_ID}`, or `./.har/verify.sh ${AGENT_ID}`
29
+
30
+ ## Project commands
31
+
32
+ ```bash
33
+ # Build
34
+ xcodebuild build -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16' CODE_SIGNING_ALLOWED=NO
35
+
36
+ # Run unit tests
37
+ xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16' CODE_SIGNING_ALLOWED=NO
38
+
39
+ # Install app on booted simulator
40
+ xcrun simctl install booted path/to/MyApp.app
41
+
42
+ # Launch app
43
+ xcrun simctl launch booted com.example.myapp
44
+ ```
45
+
46
+ Adapt for your scheme and simulator — see `.har/harness.env`.
47
+
48
+ ## User-flow validation (RocketSim)
49
+
50
+ If the RocketSim stage template is installed (`har env add-stage rocketsim`):
51
+
52
+ ```bash
53
+ ./.har/stages/rocketsim-flows.sh ${AGENT_ID}
54
+ # included in:
55
+ ./.har/verify.sh ${AGENT_ID} --full
56
+ ```
57
+
58
+ Add and edit user flow scripts in `flows/`. Read `.har/stages/ROCKETSIM.md` for the full authoring guide.
59
+
60
+ ## Do not
61
+
62
+ - Work around a failing harness command with ad-hoc setup — fix the harness or report the failure
63
+ - Edit `.env.agent.${AGENT_ID}` by hand
64
+ - Run verify before launch (the simulator and worktree must be set up first)
65
+ - Edit the main checkout — all edits go under the session work dir
@@ -0,0 +1,88 @@
1
+ # .har — Agent Harness (iOS mobile app profile)
2
+
3
+ This directory is the **agent harness** for this iOS mobile app repository. It lets AI coding agents build, test, and validate the app in isolated git worktrees against a running iOS Simulator.
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 Xcode/simulator 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: Xcode scheme, simulator name, bundle ID, infra flags |
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 — gitignored |
19
+ | `artifacts/` | Stage outputs: test results, screenshots, logs |
20
+ | `agent-slot.sh` | Shared agent-id validation and slot registry helpers |
21
+ | `setup-infra.sh` | Boot the iOS Simulator; start optional Docker services |
22
+ | `launch.sh` | Launch one agent slot (git worktree, CocoaPods/SPM deps, env file) |
23
+ | `verify.sh` | Verification pipeline (build + unit-tests; --full adds lint + user-flow validation) |
24
+ | `teardown.sh` | Tear down one agent slot (worktree + env file) |
25
+ | `agent-cli.sh` | Inspect slot status, run xcodebuild commands, install/launch app |
26
+ | `docker-compose.agent.yml` | Optional shared backend services |
27
+ | `CLAUDE.agent.md` | Detailed instructions for coding agents |
28
+ | `justfile` | Optional shortcuts (requires `just`) |
29
+
30
+ No PM2 or web-port wiring in this profile — agents run xcodebuild 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 # boots the iOS Simulator
49
+ ./.har/launch.sh 1
50
+ ./.har/verify.sh 1 # build + unit-tests
51
+ ./.har/verify.sh 1 --full # + lint + rocketsim-flows (if installed)
52
+ ./.har/teardown.sh 1
53
+ ```
54
+
55
+ ## User-flow validation
56
+
57
+ Add the RocketSim stage template to get reusable UI flow validation:
58
+
59
+ ```bash
60
+ har env add-stage rocketsim
61
+ ```
62
+
63
+ This installs `.har/stages/rocketsim-flows.sh` and a `flows/` directory. Add flow scripts under `flows/` (see `.har/stages/ROCKETSIM.md`). Full verify automatically runs all flows.
64
+
65
+ ## Verification contract
66
+
67
+ | Mode | Command | Typical steps |
68
+ |------|---------|---------------|
69
+ | Quick | `har env verify <id>` | build, unit-tests |
70
+ | Full | `har env verify <id> --full` | + lint, **rocketsim-flows** when installed |
71
+
72
+ ## Configuration
73
+
74
+ Edit **`harness.env`** to set:
75
+ - `HARNESS_XCODE_SCHEME` — your app's shared Xcode scheme
76
+ - `HARNESS_XCODE_WORKSPACE` or `HARNESS_XCODE_PROJECT` — path to the project file
77
+ - `HARNESS_SIMULATOR_NAME` — target simulator (must be listed in `xcrun simctl list devices`)
78
+ - `HARNESS_BUNDLE_ID` — app bundle identifier
79
+
80
+ ## Session lifecycle
81
+
82
+ Every `launch` starts a **fresh session**: a new git worktree from the current HEAD at
83
+ `~/worktrees/<base-branch>-<sha4>-har-agent-<id>-<rand4>`, on a branch of the same name.
84
+ The session is recorded in `.har/slots/agent-<id>.json`.
85
+
86
+ - Relaunching a slot **replaces** its previous session; uncommitted changes require `--force`.
87
+ - `teardown` removes the worktree but **keeps the session branch** for push / PR.
88
+ - `har env complete <id>` finishes a session: full verify + teardown, branch kept.
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ # Namespaced CLI for managing an iOS mobile app 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
+
22
+ resolve_work_dir() {
23
+ local env_file
24
+ env_file="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
25
+ echo "No active environment for agent ${AGENT_ID}" >&2
26
+ echo " Run: ./.har/launch.sh ${AGENT_ID}" >&2
27
+ exit 1
28
+ }
29
+ # shellcheck source=/dev/null
30
+ source "$env_file"
31
+ resolve_agent_work_dir "$env_file" "$AGENT_ID"
32
+ }
33
+
34
+ case "$COMMAND" in
35
+ status)
36
+ ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT" || true)"
37
+
38
+ if [ -n "$ENV_FILE" ]; then
39
+ # shellcheck source=/dev/null
40
+ source "$ENV_FILE"
41
+ echo "Agent ${AGENT_ID}: active"
42
+ echo " Work dir: $(resolve_agent_work_dir "$ENV_FILE" "$AGENT_ID")"
43
+ [ -n "$WORKTREE_DIR" ] && [ -d "$WORKTREE_DIR" ] && echo " Worktree: $WORKTREE_DIR"
44
+ echo " Scheme: ${HARNESS_XCODE_SCHEME:-not set}"
45
+ echo " Simulator: ${HARNESS_SIMULATOR_NAME:-not set}"
46
+ echo " Bundle ID: ${HARNESS_BUNDLE_ID:-not set}"
47
+ else
48
+ echo "No active environment for agent ${AGENT_ID}"
49
+ echo " Run: ./.har/launch.sh ${AGENT_ID}"
50
+ fi
51
+ ;;
52
+
53
+ simulator)
54
+ # Show simulator state and UDID for the configured device.
55
+ SIM_NAME="${HARNESS_SIMULATOR_NAME:-iPhone 16}"
56
+ echo "Simulator: ${SIM_NAME}"
57
+ xcrun simctl list devices available 2>/dev/null \
58
+ | grep "${SIM_NAME}" \
59
+ | grep -E '\([A-F0-9-]{36}\)' \
60
+ | head -5 \
61
+ || echo " (no matching simulator found — check HARNESS_SIMULATOR_NAME in harness.env)"
62
+ ;;
63
+
64
+ logs)
65
+ echo "iOS profile has no managed processes." >&2
66
+ echo "Use 'exec' to run project commands, or check Console.app for simulator logs." >&2
67
+ exit 1
68
+ ;;
69
+
70
+ restart)
71
+ echo "iOS profile has no managed processes to restart." >&2
72
+ echo "Relaunch the slot or reinstall the app with:" >&2
73
+ echo " ./.har/agent-cli.sh ${AGENT_ID} exec xcrun simctl install booted <path-to.app>" >&2
74
+ exit 1
75
+ ;;
76
+
77
+ install)
78
+ APP_PATH="${3:-}"
79
+ if [ -z "$APP_PATH" ]; then
80
+ echo "Usage: agent-cli.sh ${AGENT_ID} install <path-to.app>" >&2
81
+ exit 1
82
+ fi
83
+ echo "==> Installing ${APP_PATH} on simulator..."
84
+ xcrun simctl install booted "$APP_PATH"
85
+ echo "✓ Installed"
86
+ ;;
87
+
88
+ launch-app)
89
+ BUNDLE="${3:-${HARNESS_BUNDLE_ID:-}}"
90
+ if [ -z "$BUNDLE" ]; then
91
+ echo "Usage: agent-cli.sh ${AGENT_ID} launch-app [bundle-id]" >&2
92
+ echo " or set HARNESS_BUNDLE_ID in harness.env" >&2
93
+ exit 1
94
+ fi
95
+ echo "==> Launching ${BUNDLE} on simulator..."
96
+ xcrun simctl launch booted "$BUNDLE"
97
+ ;;
98
+
99
+ url)
100
+ WORK_DIR="$(resolve_work_dir 2>/dev/null || echo "$REPO_ROOT")"
101
+ echo "Work dir: $WORK_DIR"
102
+ [ -d "$WORKTREE_DIR" ] && echo "Worktree: $WORKTREE_DIR"
103
+ echo "Scheme: ${HARNESS_XCODE_SCHEME:-not set}"
104
+ echo "Simulator: ${HARNESS_SIMULATOR_NAME:-not set}"
105
+ echo "Bundle ID: ${HARNESS_BUNDLE_ID:-not set}"
106
+ har_infra_enabled mock-server && echo "Mock: check docker-compose.agent.yml for port"
107
+ ;;
108
+
109
+ exec)
110
+ shift 2
111
+ if [ $# -eq 0 ]; then
112
+ echo "Usage: agent-cli.sh ${AGENT_ID} exec <command>" >&2
113
+ exit 1
114
+ fi
115
+ WORK_DIR="$(resolve_work_dir)"
116
+ bash -c "cd '$WORK_DIR' && $*"
117
+ ;;
118
+
119
+ *)
120
+ echo "Unknown command: $COMMAND" >&2
121
+ echo ""
122
+ echo "Commands: status, simulator, install <app>, launch-app [bundle-id], url, exec <cmd>"
123
+ exit 1
124
+ ;;
125
+ esac