@aarwitz/tapp 0.17.0-rc.8 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +34 -0
- package/.claude-plugin/plugin.json +33 -0
- package/AGENTS.md +34 -14
- package/Harness/OCQAHarnessUITests/ExplorerTests.swift +11 -0
- package/README.md +126 -77
- package/bin/tapp.js +187 -63
- package/docs/BROWSER-PRODUCT.md +1 -1
- package/docs/application-model.md +12 -3
- package/mcp-server/src/android-explorer.js +3 -1
- package/mcp-server/src/android-flow.js +18 -1
- package/mcp-server/src/application-model.js +83 -13
- package/mcp-server/src/ci-report.js +2 -2
- package/mcp-server/src/ci-setup.js +4 -4
- package/mcp-server/src/environment-preflight.js +43 -0
- package/mcp-server/src/html-report.js +3 -2
- package/mcp-server/src/index.js +326 -110
- package/mcp-server/src/pr-selection.js +2 -2
- package/mcp-server/src/product-operations.js +111 -6
- package/mcp-server/src/report.js +17 -4
- package/mcp-server/src/web-explorer.js +123 -10
- package/mcp-server/src/web-flow.js +17 -1
- package/package.json +6 -4
- package/scripts/ci-gate.sh +42 -0
- package/scripts/flow_lib.py +1 -1
- package/scripts/quick-capture.sh +2 -2
- package/skills/tapp/SKILL.md +75 -0
- package/skills/tapp/agents/openai.yaml +4 -0
- package/skills/tapp/references/commands.md +102 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tapp",
|
|
3
|
+
"description": "The official Tapp plugin: the agent skill and MCP tools for testing real iOS, Android, and web app surfaces.",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Aaron Horowitz",
|
|
6
|
+
"url": "https://github.com/aarwitz"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "tapp",
|
|
11
|
+
"source": {
|
|
12
|
+
"source": "npm",
|
|
13
|
+
"package": "@aarwitz/tapp"
|
|
14
|
+
},
|
|
15
|
+
"description": "Give Claude hands and eyes on iOS, Android, and web apps, with exploration, replayable flows, evidence, and deterministic CI gates.",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Aaron Horowitz",
|
|
18
|
+
"url": "https://github.com/aarwitz"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://runtapp.com/",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"ios",
|
|
24
|
+
"simulator",
|
|
25
|
+
"testing",
|
|
26
|
+
"qa",
|
|
27
|
+
"xcuitest",
|
|
28
|
+
"mcp",
|
|
29
|
+
"agent-skill",
|
|
30
|
+
"copilot"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tapp",
|
|
3
|
+
"description": "Give Claude hands and eyes on iOS, Android, and web apps, with exploration, replayable flows, evidence, and deterministic CI gates.",
|
|
4
|
+
"version": "0.17.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Aaron Horowitz",
|
|
7
|
+
"url": "https://github.com/aarwitz"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://runtapp.com/",
|
|
10
|
+
"repository": "https://github.com/aarwitz/tapp",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ios",
|
|
14
|
+
"simulator",
|
|
15
|
+
"testing",
|
|
16
|
+
"qa",
|
|
17
|
+
"xcuitest",
|
|
18
|
+
"mcp",
|
|
19
|
+
"agent-skill",
|
|
20
|
+
"copilot"
|
|
21
|
+
],
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"tapp": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": [
|
|
26
|
+
"-y",
|
|
27
|
+
"@aarwitz/tapp@0.17.0",
|
|
28
|
+
"mcp"
|
|
29
|
+
],
|
|
30
|
+
"cwd": "${CLAUDE_PROJECT_DIR}"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/AGENTS.md
CHANGED
|
@@ -1,26 +1,40 @@
|
|
|
1
1
|
# Tapp agent playbook
|
|
2
2
|
|
|
3
|
-
You (the agent) have Tapp: hands
|
|
4
|
-
|
|
3
|
+
You (the agent) have Tapp: hands and eyes on real app surfaces — iOS simulators, Android
|
|
4
|
+
emulators/devices, plus (beta) web apps in a real browser. Release judgment belongs only to Tapp's
|
|
5
|
+
repository-connected deterministic gate.
|
|
5
6
|
|
|
6
7
|
## No MCP connected? Just run the CLI
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
The core inspect, explore, replay, and gate capabilities work as plain commands — no Tapp account,
|
|
10
|
+
server, or global install. `[target]` is
|
|
9
11
|
optional: with nothing, tapp finds + builds the Xcode project in the cwd (or falls back to
|
|
10
12
|
the app already on the simulator); it also accepts a repo dir, a `path/to/App.app`, a
|
|
11
13
|
bundle id, or (web) an http(s) URL. You never need to know a bundle id up front.
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
npx -y @aarwitz/tapp explore [target] # autonomous exploration → findings + evidence (observation, not a gate; ≈ tapp_explore)
|
|
15
|
-
npx -y @aarwitz/tapp
|
|
16
|
-
npx -y @aarwitz/tapp
|
|
17
|
-
npx -y @aarwitz/tapp
|
|
18
|
-
npx -y @aarwitz/tapp
|
|
19
|
-
npx -y @aarwitz/tapp
|
|
20
|
-
npx -y @aarwitz/tapp
|
|
21
|
-
npx -y @aarwitz/tapp
|
|
16
|
+
npx -y @aarwitz/tapp@latest explore [target] # autonomous exploration → findings + evidence (observation, not a gate; ≈ tapp_explore)
|
|
17
|
+
npx -y @aarwitz/tapp@latest explore https://your-app.example --watch # web: visibly follow the same exploration
|
|
18
|
+
npx -y @aarwitz/tapp@latest open [target] # launch + screen summary + screenshot saved to a file (≈ tapp_open_app)
|
|
19
|
+
npx -y @aarwitz/tapp@latest tree [target] # accessibility tree, --json for every element (≈ tapp_ui_tree)
|
|
20
|
+
npx -y @aarwitz/tapp@latest shot # screenshot the booted sim → file path (≈ tapp_screenshot)
|
|
21
|
+
npx -y @aarwitz/tapp@latest apps # what's installed on the simulator, with bundle ids
|
|
22
|
+
npx -y @aarwitz/tapp@latest build [dir] # build the app in an Xcode repo + install it (≈ tapp_build)
|
|
23
|
+
npx -y @aarwitz/tapp@latest explore app.apk --platform android --app-id com.acme.app
|
|
24
|
+
npx -y @aarwitz/tapp@latest flow run .tapp/flows/smoke.yml # committed, keyless E2E replay
|
|
22
25
|
```
|
|
23
26
|
|
|
27
|
+
If repository onboarding detects multiple application targets, target detection is deterministic but
|
|
28
|
+
the choice is the user's. Explicit `init --explore` asks even when the model has a saved default; a
|
|
29
|
+
later bare `explore` may consume that default. In a human TTY, Tapp displays a numbered selector and
|
|
30
|
+
continues in the same command. A non-interactive CLI prints the exact choices and exits before
|
|
31
|
+
building. MCP returns
|
|
32
|
+
`reason: "target-selection-required"` with structured `choices[]` (`platform`, `name`, `sourcePath`,
|
|
33
|
+
`selector`, and exact `command`). **Do not pick one yourself.** Present those choices to the user
|
|
34
|
+
with the client's native multiple-choice question UI when available, then rerun using the selected
|
|
35
|
+
`--platform` and `--target`. Plain chat can list the same choices when the client has no question
|
|
36
|
+
widget.
|
|
37
|
+
|
|
24
38
|
For focused web evidence, `open` and `tree` accept one semantic interaction plus an async content
|
|
25
39
|
wait: `tapp open https://example.com --tap "Not now" --wait-for "Dashboard"`. Tapp waits for the
|
|
26
40
|
page to stabilize before capturing it and warns honestly if the bounded wait ends while it is still
|
|
@@ -32,7 +46,9 @@ that IS the screen. If you cannot (Cursor, VS Code Copilot), connect the MCP ser
|
|
|
32
46
|
instead: its tool results carry the screenshot inline. Screen *recordings* are for the
|
|
33
47
|
human: on **iOS**, `tapp explore` records the full exploration and embeds it in the report.html
|
|
34
48
|
evidence page (Android does not currently record video) — tell the user the report path so they can
|
|
35
|
-
watch it.
|
|
49
|
+
watch it. On **web**, explicit `--watch` opens the isolated Playwright Chromium window and overlays
|
|
50
|
+
Tapp's current action and pointer; the overlay is omitted from evidence screenshots. It does not
|
|
51
|
+
drive the person's existing/default browser profile.
|
|
36
52
|
|
|
37
53
|
The interactive session/record loop is MCP-only (it needs a long-lived process). Flow replay is
|
|
38
54
|
also available in the CLI. The rest of this playbook assumes the `tapp_*` MCP tools are connected. With
|
|
@@ -43,7 +59,7 @@ installs, returns the bundle id) → `tapp_explore {appBundleId}`.
|
|
|
43
59
|
|
|
44
60
|
| The user wants… | Use | NOT |
|
|
45
61
|
|---|---|---|
|
|
46
|
-
| "Show me / screenshot a screen" | `tapp_open_app` (launch + screenshot + tree, ~15s) | `tapp_explore` (a full multi-minute
|
|
62
|
+
| "Show me / screenshot a screen" | `tapp_open_app` (launch + screenshot + tree, ~15s) | `tapp_explore` (a full multi-minute exploration) |
|
|
47
63
|
| "Tap through / drive / fill a form / log in" | `tapp_session_start` → `session_act` loop | repeated `open_app` calls (cold relaunch each time) |
|
|
48
64
|
| "Is my app broken? Find bugs" | `tapp_explore` — `appBundleId` for iOS, `androidAppId` for Android, `url` for owned web apps; returns an observation (findings + evidence), not a ship verdict — gate a merge with the CI gate (`tapp ci` CLI / the GitHub Action) + a contract | a manual session (exploration is autonomous) |
|
|
49
65
|
| "Make this flow a repeatable test" | drive it in a session, then `tapp_flow_save`; replay with `tapp_flow_run` | re-driving it by hand every time |
|
|
@@ -53,6 +69,7 @@ installs, returns the bundle id) → `tapp_explore {appBundleId}`.
|
|
|
53
69
|
|
|
54
70
|
```
|
|
55
71
|
tapp_session_start { appBundleId: "com.acme.app" } → fresh launch + initial tree
|
|
72
|
+
tapp_session_act { action: "login", email: "qa@x.com", password: "…" } → atomic fill + submit + verify
|
|
56
73
|
tapp_session_act { action: "tap", id: "Email" } → tap by a11y id OR visible label
|
|
57
74
|
tapp_session_act { action: "type", text: "qa@x.com" } → types into the focused field
|
|
58
75
|
tapp_session_act { action: "tap", id: "Sign In" }
|
|
@@ -76,7 +93,8 @@ Rules that prevent 90% of failures:
|
|
|
76
93
|
3. **`wait` after anything async** (navigation, network loads): `{action: "wait", id|text, timeoutMs}`.
|
|
77
94
|
Never assume the next screen is instantly there.
|
|
78
95
|
4. **Tap the field before typing** — `type` goes to the focused field. Tap email → type email →
|
|
79
|
-
tap password → type password.
|
|
96
|
+
tap password → type password. For sign-in, prefer the atomic `login` action: it records a
|
|
97
|
+
secret-templated replay step and avoids native secure-field refocus behavior.
|
|
80
98
|
5. Tap results: `ok` (landed), `not_hittable` (exists but disabled/covered — the harness
|
|
81
99
|
auto-dismisses keyboards and retries), `not_found` (nothing matches — re-read the tree).
|
|
82
100
|
6. One session at a time. `session_start` always begins from a fresh app launch.
|
|
@@ -120,6 +138,8 @@ without a coding agent, model, subscription, or API key. AI generation and `asse
|
|
|
120
138
|
deterministic assertions, same result every time. A failed assertion is a finding.
|
|
121
139
|
- **Generate:** `tapp_flow_generate { goal: "log in and add the first item to cart" }` —
|
|
122
140
|
grounded in the app's actually-explored screens, so it can't invent steps.
|
|
141
|
+
- **Discover the file format without MCP:** `npx -y @aarwitz/tapp@latest flow example` prints a
|
|
142
|
+
complete starter Flow; `tapp flow validate <file>` checks it without launching a target.
|
|
123
143
|
|
|
124
144
|
## Setup facts (tell the user when relevant)
|
|
125
145
|
|
|
@@ -765,6 +765,12 @@ class ExplorerTests: XCTestCase {
|
|
|
765
765
|
case "type":
|
|
766
766
|
status = sessionType(value, id: target.isEmpty ? nil : target) ? "pass" : "fail"
|
|
767
767
|
if status == "fail" { detail = "no field ‘\(target)’ to type into" }
|
|
768
|
+
case "login":
|
|
769
|
+
let email = subst((step["email"] as? String) ?? "$TEST_EMAIL")
|
|
770
|
+
let password = subst((step["password"] as? String) ?? "$TEST_PASSWORD")
|
|
771
|
+
let result = sessionLogin(email: email, password: password)
|
|
772
|
+
status = result.status == "ok" ? "pass" : "fail"
|
|
773
|
+
if status == "fail" { detail = result.detail.isEmpty ? result.status : result.detail }
|
|
768
774
|
case "swipe":
|
|
769
775
|
switch target.lowercased() { case "down": app.swipeDown(); case "left": app.swipeLeft(); case "right": app.swipeRight(); default: app.swipeUp() }
|
|
770
776
|
case "back":
|
|
@@ -1029,6 +1035,11 @@ class ExplorerTests: XCTestCase {
|
|
|
1029
1035
|
|
|
1030
1036
|
let testEmail = resolve("OCQA_TEST_EMAIL")
|
|
1031
1037
|
let testPassword = resolve("OCQA_TEST_PASSWORD")
|
|
1038
|
+
if resolve("OCQA_CREDENTIALS_EXPLICIT") == "1" {
|
|
1039
|
+
// Presence only: never print, persist, or expose credential values. Report rebuilding
|
|
1040
|
+
// needs this durable marker to distinguish "not supplied" from "supplied but unused".
|
|
1041
|
+
print("OCQA_STATE:credentials_supplied")
|
|
1042
|
+
}
|
|
1032
1043
|
|
|
1033
1044
|
// --- Explicit login replay (config-driven): a recorded type/tap/wait sequence for custom
|
|
1034
1045
|
// login UIs the heuristic preamble below can't parse. When configured it takes precedence. ---
|
package/README.md
CHANGED
|
@@ -1,35 +1,76 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Tapp
|
|
2
2
|
|
|
3
3
|
[](https://github.com/aarwitz/tapp/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/@aarwitz/tapp)
|
|
5
5
|
[](https://www.npmjs.com/package/@aarwitz/tapp)
|
|
6
6
|
[](./LICENSE)
|
|
7
|
-
[](https://skills.sh/aarwitz/tapp)
|
|
8
|
+
[](https://marketplace.visualstudio.com/items?itemName=lidi-solutions.tapp)
|
|
9
|
+
[](cursor://anysphere.cursor-deeplink/mcp/install?name=tapp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBhYXJ3aXR6L3RhcHBAbGF0ZXN0IiwibWNwIl19)
|
|
10
|
+
[](https://insiders.vscode.dev/redirect/mcp/install?name=tapp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40aarwitz%2Ftapp%40latest%22%2C%22mcp%22%5D%7D)
|
|
11
|
+
|
|
12
|
+
**Tapp gives coding agents hands and eyes on real iOS, Android, and web apps.** It can inspect and
|
|
13
|
+
drive screens, explore for technical failures, save journeys as deterministic tests, and gate
|
|
14
|
+
reviewed behavior in CI.
|
|
15
|
+
|
|
16
|
+
Exploration reports findings, coverage, evidence, and limits. Only the repository-connected gate
|
|
17
|
+
returns `pass`, `fail`, or `inconclusive`. Tapp does not turn an autonomous crawl into a subjective
|
|
18
|
+
"ship-ready" score.
|
|
19
|
+
|
|
20
|
+
Supported targets:
|
|
21
|
+
|
|
22
|
+
| Target app | Runtime |
|
|
23
|
+
|---|---|
|
|
24
|
+
| iOS | Simulator on macOS with Xcode; driven through XCUITest and accessibility |
|
|
25
|
+
| Android | Connected emulator or device with `adb`; driven through UIAutomator |
|
|
26
|
+
| Web (beta) | Owned browser app in Playwright Chromium |
|
|
27
|
+
|
|
28
|
+
Windows can host Android and web testing when their prerequisites are installed. Windows desktop
|
|
29
|
+
UI applications such as WinForms, WPF, and WinUI are not currently Tapp targets.
|
|
30
|
+
|
|
31
|
+
## Give Tapp to your coding agent
|
|
32
|
+
|
|
33
|
+
After setup, the whole user prompt is:
|
|
34
|
+
|
|
35
|
+
> Use Tapp to test this app.
|
|
36
|
+
|
|
37
|
+
The official skill teaches the agent to choose the smallest useful operation, handle repositories
|
|
38
|
+
with multiple app targets, inspect visual evidence, and keep exploration observations separate from
|
|
39
|
+
gate decisions.
|
|
40
|
+
|
|
41
|
+
**Claude Code, Codex, Cursor, Copilot, and other Agent Skills clients (recommended):**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx -y skills add aarwitz/tapp --skill tapp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This installs the open Agent Skills workflow into the current project and lets the agent run the npm
|
|
48
|
+
CLI directly; no MCP server, plugin, account, API key, global Tapp install, or pasted prompt block is
|
|
49
|
+
required. Add `-g` for a user-wide install, or `--agent claude-code`, `--agent codex`, and similar
|
|
50
|
+
selectors to constrain the clients. Start or restart the agent from the application repository and
|
|
51
|
+
use the short prompt above.
|
|
52
|
+
|
|
53
|
+
**Claude Code — optional enhanced skill and MCP tools:**
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
claude plugin marketplace add aarwitz/tapp
|
|
57
|
+
claude plugin install tapp@tapp
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The plugin bundles the same `tapp` Agent Skill with the matching npm-backed MCP server. Use it when
|
|
61
|
+
you want inline screenshot tool results and a persistent interactive tap/read/type session; it is
|
|
62
|
+
not required for the core skill-to-CLI workflow.
|
|
63
|
+
|
|
64
|
+
**No agent integration:** run the npm package directly from an app repository in one line:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx -y @aarwitz/tapp@latest init . --explore
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**VS Code:** install [Tapp from the Marketplace](https://marketplace.visualstudio.com/items?itemName=lidi-solutions.tapp).
|
|
71
|
+
It contributes the same cross-platform Agent Skill to Copilot plus focused iOS simulator tools and
|
|
72
|
+
an auto-refreshing screenshot preview. The preview is not an embedded Simulator or video stream.
|
|
73
|
+
Android and web remain available through the skill's CLI/MCP workflow.
|
|
33
74
|
|
|
34
75
|
```
|
|
35
76
|
you: "Add a logout button to the settings screen"
|
|
@@ -38,7 +79,7 @@ agent: *tapp: builds, opens the app, navigates to Settings, screenshots it*
|
|
|
38
79
|
agent: "Done — and here it is working on the simulator: [screenshot]"
|
|
39
80
|
```
|
|
40
81
|
|
|
41
|
-
##
|
|
82
|
+
## npm CLI quickstart
|
|
42
83
|
|
|
43
84
|
Requirements: **Node ≥ 18**. iOS needs **macOS + Xcode**; Android needs `adb` plus a connected
|
|
44
85
|
emulator/device; web needs Playwright + Chromium.
|
|
@@ -46,14 +87,16 @@ emulator/device; web needs Playwright + Chromium.
|
|
|
46
87
|
From the app repository, let the agent see the current screen and then explore it:
|
|
47
88
|
|
|
48
89
|
```bash
|
|
49
|
-
npx -y @aarwitz/tapp open # builds/launches as needed; prints a screenshot path + screen summary
|
|
50
|
-
npx -y @aarwitz/tapp explore # explores the real app; prints findings + evidence (an observation, not a gate)
|
|
90
|
+
npx -y @aarwitz/tapp@latest open # builds/launches as needed; prints a screenshot path + screen summary
|
|
91
|
+
npx -y @aarwitz/tapp@latest explore # explores the real app; prints findings + evidence (an observation, not a gate)
|
|
51
92
|
```
|
|
52
93
|
|
|
53
94
|
Claude Code can read the saved image with its file-reading tool; Codex can open it with
|
|
54
95
|
`view_image`. The agent should report what the screenshot proves, relay the exploration findings
|
|
55
96
|
as-is (an observation, not a merge decision — `tapp ci` gates that), and link the HTML evidence
|
|
56
|
-
report. No server, account,
|
|
97
|
+
report. No Tapp server, account, global install, API key, or bundle id is required for this loop.
|
|
98
|
+
The platform runtime still needs to be installed, and repository onboarding can write reviewed
|
|
99
|
+
configuration under `.tapp/`.
|
|
57
100
|
|
|
58
101
|
The product, executable, and package leaf are all Tapp: npm distributes it as
|
|
59
102
|
`@aarwitz/tapp`, while the installed command remains `tapp`.
|
|
@@ -62,24 +105,33 @@ To bootstrap maintained release infrastructure, preview the repository model and
|
|
|
62
105
|
before Tapp writes anything:
|
|
63
106
|
|
|
64
107
|
```bash
|
|
65
|
-
npx -y @aarwitz/tapp init . --dry-run --json-out /tmp/tapp-init.json
|
|
108
|
+
npx -y @aarwitz/tapp@latest init . --dry-run --json-out /tmp/tapp-init.json
|
|
66
109
|
# Build/start the detected web target, ground the first UI Map, then stop it.
|
|
67
|
-
npx -y @aarwitz/tapp init . --explore --platform web
|
|
110
|
+
npx -y @aarwitz/tapp@latest init . --explore --platform web
|
|
68
111
|
# Or build/install the detected Xcode target, ground the map, and persist the validated scheme.
|
|
69
|
-
npx -y @aarwitz/tapp init . --explore --platform ios --target .
|
|
112
|
+
npx -y @aarwitz/tapp@latest init . --explore --platform ios --target .
|
|
70
113
|
# Or connect to an already-running owned URL:
|
|
71
|
-
npx -y @aarwitz/tapp init . --explore --platform web --url http://127.0.0.1:4173
|
|
114
|
+
npx -y @aarwitz/tapp@latest init . --explore --platform web --url http://127.0.0.1:4173
|
|
72
115
|
# If the app has roles/accounts, bind names once; values stay in local/CI secrets.
|
|
73
|
-
npx -y @aarwitz/tapp actor set alice . --role member --session isolated \
|
|
116
|
+
npx -y @aarwitz/tapp@latest actor set alice . --role member --session isolated \
|
|
74
117
|
--credential email=ALICE_EMAIL --credential password=ALICE_PASSWORD
|
|
75
118
|
# Review-only path: tapp init . → tapp plan show → tapp plan review --approve ...
|
|
76
119
|
|
|
77
120
|
# After approved drafts replay and are promoted, establish the selected target's baseline
|
|
78
121
|
# through the ordinary full gate, then generate the reviewable GitHub workflow.
|
|
79
|
-
npx -y @aarwitz/tapp baseline create . --platform web
|
|
80
|
-
npx -y @aarwitz/tapp ci install .
|
|
122
|
+
npx -y @aarwitz/tapp@latest baseline create . --platform web
|
|
123
|
+
npx -y @aarwitz/tapp@latest ci install .
|
|
81
124
|
```
|
|
82
125
|
|
|
126
|
+
In a repository containing multiple apps (for example, iOS plus web),
|
|
127
|
+
`tapp init . --explore` without an explicit target does not guess from detection order—even when a
|
|
128
|
+
prior choice is recorded. A human terminal gets a numbered
|
|
129
|
+
selector; a non-interactive CLI prints exact target-selection commands, while MCP also returns
|
|
130
|
+
structured choices. Neither builds or writes before the choice. After you choose one, the model
|
|
131
|
+
retains every detected target and records the choice as the default for the next bare `tapp explore`;
|
|
132
|
+
explicit `init --explore` continues to ask because it is the onboarding/refresh operation. Setup gaps
|
|
133
|
+
belonging only to unselected targets are shown as informational, not as failures of the selected run.
|
|
134
|
+
|
|
83
135
|
The baseline command writes only after exploration and every selected deterministic suite pass
|
|
84
136
|
conclusively. It stores `.tapp/baselines/<platform>/<target-id>.json`; the generated workflow
|
|
85
137
|
uses that exact target identity so two apps on the same platform never share a baseline. `ci
|
|
@@ -92,42 +144,50 @@ Every verb takes whatever you have: nothing (auto-detects the repo you're in, or
|
|
|
92
144
|
already on the simulator), a repo directory, a `path/to/App.app`, or a bundle id:
|
|
93
145
|
|
|
94
146
|
```bash
|
|
95
|
-
npx -y @aarwitz/tapp open [target] # launch the app → screen summary + screenshot file
|
|
96
|
-
npx -y @aarwitz/tapp tree [target] # accessibility tree of the current screen
|
|
97
|
-
npx -y @aarwitz/tapp shot # screenshot the booted simulator
|
|
98
|
-
npx -y @aarwitz/tapp apps # what's installed on the simulator (names + bundle ids)
|
|
99
|
-
npx -y @aarwitz/tapp build [dir] # just build + install (scheme auto-detected)
|
|
147
|
+
npx -y @aarwitz/tapp@latest open [target] # launch the app → screen summary + screenshot file
|
|
148
|
+
npx -y @aarwitz/tapp@latest tree [target] # accessibility tree of the current screen
|
|
149
|
+
npx -y @aarwitz/tapp@latest shot # screenshot the booted simulator
|
|
150
|
+
npx -y @aarwitz/tapp@latest apps # what's installed on the simulator (names + bundle ids)
|
|
151
|
+
npx -y @aarwitz/tapp@latest build [dir] # just build + install (scheme auto-detected)
|
|
100
152
|
```
|
|
101
153
|
|
|
102
|
-
Web (beta): `npx -y @aarwitz/tapp explore http://localhost:3000` *(one-time setup:
|
|
103
|
-
`npm i -g playwright && npx playwright install chromium`)
|
|
154
|
+
Web (beta): `npx -y @aarwitz/tapp@latest explore http://localhost:3000` *(one-time setup:
|
|
155
|
+
`npm i -g playwright && npx playwright install chromium`)*. Add `--watch` to open Tapp's controlled,
|
|
156
|
+
isolated Chromium window and follow its clicks with an on-page pointer/action label. Tapp hides that
|
|
157
|
+
watch UI from saved evidence screenshots and does not automate your personal/default browser profile.
|
|
104
158
|
|
|
105
159
|
Focused web inspection waits briefly for loading states to settle. If a consent or location modal
|
|
106
160
|
blocks the screen, dismiss it and wait for the content you care about in the same package-only call:
|
|
107
161
|
|
|
108
162
|
```bash
|
|
109
|
-
npx -y @aarwitz/tapp open https://example.com --tap "Not now" --wait-for "Dashboard"
|
|
110
|
-
npx -y @aarwitz/tapp tree https://example.com --tap "Not now" --wait-for "Dashboard" --json
|
|
163
|
+
npx -y @aarwitz/tapp@latest open https://example.com --tap "Not now" --wait-for "Dashboard"
|
|
164
|
+
npx -y @aarwitz/tapp@latest tree https://example.com --tap "Not now" --wait-for "Dashboard" --json
|
|
111
165
|
```
|
|
112
166
|
|
|
113
167
|
Android:
|
|
114
168
|
|
|
115
169
|
```bash
|
|
116
|
-
npx -y @aarwitz/tapp explore path/to/app-debug.apk --platform android --app-id com.acme.app
|
|
117
|
-
npx -y @aarwitz/tapp open com.acme.app --platform android
|
|
170
|
+
npx -y @aarwitz/tapp@latest explore path/to/app-debug.apk --platform android --app-id com.acme.app
|
|
171
|
+
npx -y @aarwitz/tapp@latest open com.acme.app --platform android
|
|
118
172
|
```
|
|
119
173
|
|
|
120
|
-
|
|
174
|
+
Turn a driven journey into a deterministic test, or print the complete Flow format before writing
|
|
175
|
+
one by hand:
|
|
176
|
+
|
|
121
177
|
```bash
|
|
122
|
-
npx -y @aarwitz/tapp
|
|
123
|
-
npx -y @aarwitz/tapp
|
|
178
|
+
npx -y @aarwitz/tapp@latest flow example
|
|
179
|
+
npx -y @aarwitz/tapp@latest flow validate .tapp/flows/smoke.yml
|
|
180
|
+
npx -y @aarwitz/tapp@latest flow run .tapp/flows/smoke.yml
|
|
124
181
|
```
|
|
125
182
|
|
|
126
|
-
|
|
183
|
+
Flows support an atomic `login` step using `$TEST_EMAIL` / `$TEST_PASSWORD`; Tapp's session recorder
|
|
184
|
+
creates that step without writing credential values into the repository.
|
|
127
185
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
186
|
+
Optional but recommended (prebuilds the test harness so the first run is fast):
|
|
187
|
+
```bash
|
|
188
|
+
npx -y @aarwitz/tapp@latest install # ~2 min, one time
|
|
189
|
+
npx -y @aarwitz/tapp@latest doctor # verify Xcode / simulators / toolchain
|
|
190
|
+
```
|
|
131
191
|
|
|
132
192
|
### MCP hookup (optional)
|
|
133
193
|
|
|
@@ -137,14 +197,14 @@ context** (the model literally sees the screen) and the **interactive session lo
|
|
|
137
197
|
|
|
138
198
|
**Claude Code:**
|
|
139
199
|
```bash
|
|
140
|
-
claude mcp add tapp -- npx -y @aarwitz/tapp mcp
|
|
200
|
+
claude mcp add tapp -- npx -y @aarwitz/tapp@latest mcp
|
|
141
201
|
```
|
|
142
202
|
|
|
143
203
|
**Cursor / VS Code (Copilot)** — add to `~/.cursor/mcp.json` (Cursor) or `.vscode/mcp.json` (VS Code):
|
|
144
204
|
```json
|
|
145
205
|
{
|
|
146
206
|
"servers": {
|
|
147
|
-
"tapp": { "type": "stdio", "command": "npx", "args": ["-y", "@aarwitz/tapp", "mcp"] }
|
|
207
|
+
"tapp": { "type": "stdio", "command": "npx", "args": ["-y", "@aarwitz/tapp@latest", "mcp"] }
|
|
148
208
|
}
|
|
149
209
|
}
|
|
150
210
|
```
|
|
@@ -153,10 +213,10 @@ claude mcp add tapp -- npx -y @aarwitz/tapp mcp
|
|
|
153
213
|
```toml
|
|
154
214
|
[mcp_servers.tapp]
|
|
155
215
|
command = "npx"
|
|
156
|
-
args = ["-y", "@aarwitz/tapp", "mcp"]
|
|
216
|
+
args = ["-y", "@aarwitz/tapp@latest", "mcp"]
|
|
157
217
|
```
|
|
158
218
|
|
|
159
|
-
**Any other MCP client:** stdio command `npx -y @aarwitz/tapp mcp`.
|
|
219
|
+
**Any other MCP client:** stdio command `npx -y @aarwitz/tapp@latest mcp`.
|
|
160
220
|
|
|
161
221
|
Then ask your agent:
|
|
162
222
|
> "Explore my app and show me what breaks."
|
|
@@ -185,7 +245,9 @@ Then ask your agent:
|
|
|
185
245
|
| 📱 | `tapp_list_simulators` / `boot_simulator` / `install_app` | Simulator + app management. |
|
|
186
246
|
| 🩺 | `tapp_health`, `tapp_capture*`, `tapp_parse_markers` | Diagnostics and capture history. |
|
|
187
247
|
|
|
188
|
-
|
|
248
|
+
Canonical installable skill: [`skills/tapp/SKILL.md`](skills/tapp/SKILL.md). Full low-level tool
|
|
249
|
+
reference: [AGENTS.md](./AGENTS.md). Both ship inside the npm package; installing the Claude plugin
|
|
250
|
+
or Agent Skill is what makes the workflow discoverable without pasting instructions.
|
|
189
251
|
Application-model and import contract: [`docs/application-model.md`](docs/application-model.md).
|
|
190
252
|
The desktop Coverage view reads the same `.tapp/application-model.json`,
|
|
191
253
|
`.tapp/release-plan.json`, and `.tapp/ui-map.json`, including explicit proposal review; it
|
|
@@ -297,7 +359,7 @@ accepts that `.app`, detects its bundle id, writes report artifacts, and exits n
|
|
|
297
359
|
gate fails:
|
|
298
360
|
|
|
299
361
|
```bash
|
|
300
|
-
npx -y @aarwitz/tapp ci --app path/to/MyApp.app \
|
|
362
|
+
npx -y @aarwitz/tapp@latest ci --app path/to/MyApp.app \
|
|
301
363
|
--project-dir . --pr-base origin/main --pr-head HEAD \
|
|
302
364
|
--target-key target_ios_myapp \
|
|
303
365
|
--pr-plan-out tapp-pr-plan.json \
|
|
@@ -331,12 +393,6 @@ environment. Add
|
|
|
331
393
|
[`docs/scenarios.md`](docs/scenarios.md). Automatic
|
|
332
394
|
baselines are isolated by platform and target, so two same-platform apps are never compared.
|
|
333
395
|
|
|
334
|
-
**The hosted service is under development and is not currently offered for customer repositories.**
|
|
335
|
-
Do not upload private code or credentials to an old preview. The retained
|
|
336
|
-
cloud prototype is not the production SaaS boundary. Use the local Release Studio and the portable
|
|
337
|
-
GitHub Action in infrastructure you control until the new account, tenant authorization, private
|
|
338
|
-
evidence, and isolated-worker boundary passes security review.
|
|
339
|
-
|
|
340
396
|
## Make your repo agent-verified
|
|
341
397
|
|
|
342
398
|
Drop this into your repo's `AGENTS.md` (read by Codex, Cursor, Copilot, Devin, Zed, …) so
|
|
@@ -345,9 +401,9 @@ your agent proves its UI work instead of claiming it:
|
|
|
345
401
|
```markdown
|
|
346
402
|
## Verifying UI changes
|
|
347
403
|
This repo uses tapp (https://github.com/aarwitz/tapp) to verify UI work on a real app surface
|
|
348
|
-
(iOS simulator, Android emulator/device, or a browser for web). After any UI change, run `npx -y @aarwitz/tapp open` from the
|
|
404
|
+
(iOS simulator, Android emulator/device, or a browser for web). After any UI change, run `npx -y @aarwitz/tapp@latest open` from the
|
|
349
405
|
repo root (it finds and builds the Xcode project itself) and look at the screenshot it saves as
|
|
350
|
-
proof. Before declaring a feature done, run `npx -y @aarwitz/tapp explore` (or `explore <url>` for web)
|
|
406
|
+
proof. Before declaring a feature done, run `npx -y @aarwitz/tapp@latest explore` (or `explore <url>` for web)
|
|
351
407
|
and report the findings + evidence. A change is not "done" until it has been seen working.
|
|
352
408
|
(If the tapp MCP server is connected, the tapp_* tools do the same with inline screenshots —
|
|
353
409
|
tapp_build builds + installs the app and returns the bundle id for tapp_explore.)
|
|
@@ -379,13 +435,6 @@ only an optional authoring/enrichment layer (`tapp_flow_generate`, `assert_ai`,
|
|
|
379
435
|
The first tool call builds the harness once (~2 min, cached in `~/.tapp`; rebuilt automatically
|
|
380
436
|
if you switch simulators). All captures land in `~/.tapp/captures/`.
|
|
381
437
|
|
|
382
|
-
## Desktop status
|
|
383
|
-
|
|
384
|
-
The macOS cockpit is frozen as a supported native interface and parity floor; it still reads the
|
|
385
|
-
canonical Application Model, release plan, and UI Map in Coverage. Its older import/build path is
|
|
386
|
-
not yet a thin client of the shared product-operation layer, so new product work is converging in
|
|
387
|
-
the browser without deleting or reducing the desktop experience.
|
|
388
|
-
|
|
389
438
|
## License
|
|
390
439
|
|
|
391
440
|
[MIT](./LICENSE)
|