@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.
- package/LICENSE +674 -0
- package/README.md +208 -0
- package/control/docker-compose.build.yml +7 -0
- package/control/docker-compose.yml +31 -0
- package/dist/index.js +41753 -0
- package/dist/prompts/system-authoring.md +104 -0
- package/dist/templates/AGENT.md.template +40 -0
- package/dist/templates/CLAUDE.md.template +5 -0
- package/dist/templates/adaptation-prompt-init.md +124 -0
- package/dist/templates/adaptation-prompt-maintain.md +73 -0
- package/dist/templates/cursor-rule.mdc.template +101 -0
- package/dist/templates/har-boilerplate/CLAUDE.agent.md +54 -0
- package/dist/templates/har-boilerplate/README.md +134 -0
- package/dist/templates/har-boilerplate/agent-cli.sh +172 -0
- package/dist/templates/har-boilerplate/agent-slot.sh +270 -0
- package/dist/templates/har-boilerplate/attach.sh +16 -0
- package/dist/templates/har-boilerplate/docker-compose.agent.yml +66 -0
- package/dist/templates/har-boilerplate/ecosystem.agent.template.cjs +44 -0
- package/dist/templates/har-boilerplate/env.template +37 -0
- package/dist/templates/har-boilerplate/harness.env +50 -0
- package/dist/templates/har-boilerplate/justfile +39 -0
- package/dist/templates/har-boilerplate/launch.sh +229 -0
- package/dist/templates/har-boilerplate/setup-infra.sh +102 -0
- package/dist/templates/har-boilerplate/stages/README.sh +9 -0
- package/dist/templates/har-boilerplate/stages.json +67 -0
- package/dist/templates/har-boilerplate/teardown.sh +86 -0
- package/dist/templates/har-boilerplate/verify.sh +163 -0
- package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +41 -0
- package/dist/templates/har-boilerplate-cli/README.md +112 -0
- package/dist/templates/har-boilerplate-cli/agent-cli.sh +162 -0
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +270 -0
- package/dist/templates/har-boilerplate-cli/docker-compose.agent.yml +64 -0
- package/dist/templates/har-boilerplate-cli/harness.env +40 -0
- package/dist/templates/har-boilerplate-cli/justfile +27 -0
- package/dist/templates/har-boilerplate-cli/launch.sh +132 -0
- package/dist/templates/har-boilerplate-cli/setup-infra.sh +102 -0
- package/dist/templates/har-boilerplate-cli/stages/README.sh +9 -0
- package/dist/templates/har-boilerplate-cli/stages.json +61 -0
- package/dist/templates/har-boilerplate-cli/teardown.sh +66 -0
- package/dist/templates/har-boilerplate-cli/verify.sh +113 -0
- package/dist/templates/har-boilerplate-ios/ADAPT-PROMPT.md +57 -0
- package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +65 -0
- package/dist/templates/har-boilerplate-ios/README.md +88 -0
- package/dist/templates/har-boilerplate-ios/agent-cli.sh +125 -0
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +270 -0
- package/dist/templates/har-boilerplate-ios/docker-compose.agent.yml +17 -0
- package/dist/templates/har-boilerplate-ios/harness.env +42 -0
- package/dist/templates/har-boilerplate-ios/justfile +33 -0
- package/dist/templates/har-boilerplate-ios/launch.sh +132 -0
- package/dist/templates/har-boilerplate-ios/setup-infra.sh +84 -0
- package/dist/templates/har-boilerplate-ios/stages/README.sh +12 -0
- package/dist/templates/har-boilerplate-ios/stages.json +61 -0
- package/dist/templates/har-boilerplate-ios/teardown.sh +64 -0
- package/dist/templates/har-boilerplate-ios/verify.sh +165 -0
- package/dist/templates/stage-templates/playwright/.github/workflows/playwright.yml +30 -0
- package/dist/templates/stage-templates/playwright/.har/stages/PLAYWRIGHT.md +21 -0
- package/dist/templates/stage-templates/playwright/.har/stages/browser-e2e.sh +83 -0
- package/dist/templates/stage-templates/playwright/README.md +14 -0
- package/dist/templates/stage-templates/playwright/package.fragment.json +14 -0
- package/dist/templates/stage-templates/playwright/playwright.config.js +30 -0
- package/dist/templates/stage-templates/playwright/template.manifest.json +44 -0
- package/dist/templates/stage-templates/playwright/tests/a11y/smoke.spec.js +14 -0
- package/dist/templates/stage-templates/playwright/tests/api/health.spec.js +10 -0
- package/dist/templates/stage-templates/playwright/tests/frontend/smoke.spec.js +9 -0
- package/dist/templates/stage-templates/rocketsim/.har/stages/ROCKETSIM.md +126 -0
- package/dist/templates/stage-templates/rocketsim/.har/stages/rocketsim-flows.sh +188 -0
- package/dist/templates/stage-templates/rocketsim/README.md +66 -0
- package/dist/templates/stage-templates/rocketsim/flows/README.md +36 -0
- package/dist/templates/stage-templates/rocketsim/flows/example-smoke.sh +60 -0
- package/dist/templates/stage-templates/rocketsim/template.manifest.json +38 -0
- 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
|