@aarwitz/tapp 0.17.0-rc.10 → 0.17.0-rc.12

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.
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "tapp",
3
+ "description": "The official Tapp plugin: one agent skill and MCP tools for testing iOS, Android, and web apps on real 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": "Test real iOS, Android, and web apps with Tapp. Includes the Tapp agent skill plus MCP hands, eyes, exploration, replayable Flows, and deterministic release evidence.",
16
+ "author": {
17
+ "name": "Aaron Horowitz",
18
+ "url": "https://github.com/aarwitz"
19
+ },
20
+ "homepage": "https://github.com/aarwitz/tapp",
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,32 @@
1
+ {
2
+ "name": "tapp",
3
+ "description": "Test real iOS, Android, and web apps with Tapp. Includes the Tapp agent skill plus MCP hands, eyes, exploration, replayable Flows, and deterministic release evidence.",
4
+ "version": "0.17.0-rc.12",
5
+ "author": {
6
+ "name": "Aaron Horowitz",
7
+ "url": "https://github.com/aarwitz"
8
+ },
9
+ "homepage": "https://github.com/aarwitz/tapp",
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": "node",
25
+ "args": [
26
+ "${CLAUDE_PLUGIN_ROOT}/bin/tapp.js",
27
+ "mcp"
28
+ ],
29
+ "cwd": "${CLAUDE_PROJECT_DIR}"
30
+ }
31
+ }
32
+ }
package/AGENTS.md CHANGED
@@ -12,6 +12,7 @@ bundle id, or (web) an http(s) URL. You never need to know a bundle id up front.
12
12
 
13
13
  ```bash
14
14
  npx -y @aarwitz/tapp explore [target] # autonomous exploration → findings + evidence (observation, not a gate; ≈ tapp_explore)
15
+ npx -y @aarwitz/tapp explore https://your-app.example --watch # web: visibly follow the same exploration
15
16
  npx -y @aarwitz/tapp open [target] # launch + screen summary + screenshot saved to a file (≈ tapp_open_app)
16
17
  npx -y @aarwitz/tapp tree [target] # accessibility tree, --json for every element (≈ tapp_ui_tree)
17
18
  npx -y @aarwitz/tapp shot # screenshot the booted sim → file path (≈ tapp_screenshot)
@@ -22,8 +23,10 @@ npx -y @aarwitz/tapp flow run .tapp/flows/smoke.yml # committed, keyless E2E re
22
23
  ```
23
24
 
24
25
  If repository onboarding detects multiple application targets, target detection is deterministic but
25
- the choice is the user's. In a human TTY, Tapp displays a numbered selector and continues in the same
26
- command. A non-interactive CLI prints the exact choices and exits before building. MCP returns
26
+ the choice is the user's. Explicit `init --explore` asks even when the model has a saved default; a
27
+ later bare `explore` may consume that default. In a human TTY, Tapp displays a numbered selector and
28
+ continues in the same command. A non-interactive CLI prints the exact choices and exits before
29
+ building. MCP returns
27
30
  `reason: "target-selection-required"` with structured `choices[]` (`platform`, `name`, `sourcePath`,
28
31
  `selector`, and exact `command`). **Do not pick one yourself.** Present those choices to the user
29
32
  with the client's native multiple-choice question UI when available, then rerun using the selected
@@ -41,7 +44,9 @@ that IS the screen. If you cannot (Cursor, VS Code Copilot), connect the MCP ser
41
44
  instead: its tool results carry the screenshot inline. Screen *recordings* are for the
42
45
  human: on **iOS**, `tapp explore` records the full exploration and embeds it in the report.html
43
46
  evidence page (Android does not currently record video) — tell the user the report path so they can
44
- watch it.
47
+ watch it. On **web**, explicit `--watch` opens the isolated Playwright Chromium window and overlays
48
+ Tapp's current action and pointer; the overlay is omitted from evidence screenshots. It does not
49
+ drive the person's existing/default browser profile.
45
50
 
46
51
  The interactive session/record loop is MCP-only (it needs a long-lived process). Flow replay is
47
52
  also available in the CLI. The rest of this playbook assumes the `tapp_*` MCP tools are connected. With
package/README.md CHANGED
@@ -4,8 +4,10 @@
4
4
  [![npm](https://img.shields.io/npm/v/%40aarwitz%2Ftapp?color=cb3837&label=npm)](https://www.npmjs.com/package/@aarwitz/tapp)
5
5
  [![npm downloads](https://img.shields.io/npm/dw/%40aarwitz%2Ftapp?label=downloads)](https://www.npmjs.com/package/@aarwitz/tapp)
6
6
  [![license](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
7
+ [![Agent Skill](https://img.shields.io/badge/Agent_Skill-install-6b5cff)](https://skills.sh/aarwitz/tapp)
8
+ [![VS Code extension](https://img.shields.io/badge/VS_Code-extension-0098FF)](https://marketplace.visualstudio.com/items?itemName=lidi-solutions.tapp)
7
9
  [![Install in Cursor](https://img.shields.io/badge/Cursor-Install_MCP-000000)](cursor://anysphere.cursor-deeplink/mcp/install?name=tapp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBhYXJ3aXR6L3RhcHAiLCJtY3AiXX0=)
8
- [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP-0098FF)](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%22%2C%22mcp%22%5D%7D)
10
+ [![VS Code MCP](https://img.shields.io/badge/VS_Code-Install_MCP-0098FF)](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%22%2C%22mcp%22%5D%7D)
9
11
 
10
12
  **Tapp is the release-contract and evidence layer for teams shipping agent-authored applications.**
11
13
  It turns a repository and real product into an observed UI Map, a compact reviewed deterministic
@@ -31,6 +33,47 @@ Three platforms, one observe-and-gate engine:
31
33
  autonomous exploration, the deterministic detectors (uncaught exceptions, failed requests,
32
34
  dead buttons, broken links, placeholder `href="#"` links, error pages), and the same gate.
33
35
 
36
+ ## Give Tapp to your coding agent
37
+
38
+ After setup, the whole user prompt is:
39
+
40
+ > Use Tapp to test this app.
41
+
42
+ The official skill teaches the agent to choose the smallest useful operation, handle repositories
43
+ with multiple app targets, inspect visual evidence, and keep exploration observations separate from
44
+ release judgment.
45
+
46
+ **Claude Code — skill and MCP tools together:**
47
+
48
+ ```bash
49
+ claude plugin marketplace add aarwitz/tapp
50
+ claude plugin install tapp@tapp
51
+ ```
52
+
53
+ Restart Claude Code after installation, open the application repository, and use the short prompt
54
+ above. The plugin bundles both the `tapp` Agent Skill and the local stdio MCP server; no additional
55
+ prompt block or bundle id is required.
56
+
57
+ **Claude, Codex, Cursor, Copilot, and other Agent Skills clients — skill only:**
58
+
59
+ ```bash
60
+ npx -y skills add aarwitz/tapp --skill tapp
61
+ ```
62
+
63
+ This installs the open Agent Skills version of the same instructions into the current project and
64
+ lets the agent fall back to the npm CLI when MCP is not connected. Add `-g` for a user-wide install,
65
+ or `--agent claude-code`, `--agent codex`, and similar selectors to constrain the clients.
66
+
67
+ **No agent integration:** run the npm package directly from an app repository:
68
+
69
+ ```bash
70
+ npx -y @aarwitz/tapp init . --explore
71
+ ```
72
+
73
+ **VS Code:** install [Tapp from the Marketplace](https://marketplace.visualstudio.com/items?itemName=lidi-solutions.tapp).
74
+ It contributes the same cross-platform Agent Skill to Copilot plus focused iOS simulator tools and a
75
+ live simulator panel. Android and web remain available through the skill's CLI/MCP workflow.
76
+
34
77
  ```
35
78
  you: "Add a logout button to the settings screen"
36
79
  agent: *writes the Swift*
@@ -38,7 +81,7 @@ agent: *tapp: builds, opens the app, navigates to Settings, screenshots it*
38
81
  agent: "Done — and here it is working on the simulator: [screenshot]"
39
82
  ```
40
83
 
41
- ## Quickstart for coding agents
84
+ ## npm CLI quickstart
42
85
 
43
86
  Requirements: **Node ≥ 18**. iOS needs **macOS + Xcode**; Android needs `adb` plus a connected
44
87
  emulator/device; web needs Playwright + Chromium.
@@ -80,11 +123,14 @@ npx -y @aarwitz/tapp baseline create . --platform web
80
123
  npx -y @aarwitz/tapp ci install .
81
124
  ```
82
125
 
83
- On a fresh repository containing multiple apps (for example, iOS plus web), bare
84
- `tapp init . --explore` does not guess from detection order. A human terminal gets a numbered
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
85
129
  selector; a non-interactive CLI prints exact target-selection commands, while MCP also returns
86
130
  structured choices. Neither builds or writes before the choice. After you choose one, the model
87
- retains every detected target and records the choice as the default for the next bare `tapp explore`.
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.
88
134
 
89
135
  The baseline command writes only after exploration and every selected deterministic suite pass
90
136
  conclusively. It stores `.tapp/baselines/<platform>/<target-id>.json`; the generated workflow
@@ -106,7 +152,9 @@ npx -y @aarwitz/tapp build [dir] # just build + install (scheme auto-detecte
106
152
  ```
107
153
 
108
154
  Web (beta): `npx -y @aarwitz/tapp explore http://localhost:3000` *(one-time setup:
109
- `npm i -g playwright && npx playwright install chromium`)*
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.
110
158
 
111
159
  Focused web inspection waits briefly for loading states to settle. If a consent or location modal
112
160
  blocks the screen, dismiss it and wait for the content you care about in the same package-only call:
@@ -191,7 +239,9 @@ Then ask your agent:
191
239
  | 📱 | `tapp_list_simulators` / `boot_simulator` / `install_app` | Simulator + app management. |
192
240
  | 🩺 | `tapp_health`, `tapp_capture*`, `tapp_parse_markers` | Diagnostics and capture history. |
193
241
 
194
- Full agent playbook: [AGENTS.md](./AGENTS.md) ships inside the package so agents can read it too.
242
+ Canonical installable skill: [`skills/tapp/SKILL.md`](skills/tapp/SKILL.md). Full low-level tool
243
+ reference: [AGENTS.md](./AGENTS.md). Both ship inside the npm package; installing the Claude plugin
244
+ or Agent Skill is what makes the workflow discoverable without pasting instructions.
195
245
  Application-model and import contract: [`docs/application-model.md`](docs/application-model.md).
196
246
  The desktop Coverage view reads the same `.tapp/application-model.json`,
197
247
  `.tapp/release-plan.json`, and `.tapp/ui-map.json`, including explicit proposal review; it
package/bin/tapp.js CHANGED
@@ -240,8 +240,8 @@ async function resolveTargetOrExit(engine, input) {
240
240
 
241
241
  function safeCommandUsage(verb) {
242
242
  const usage = {
243
- explore: "tapp explore [target] [--platform ios|android|web] [--actions N] [--timeout SEC] [--email VALUE] [--password VALUE] [--baseline FILE] [--json FILE]\n iOS launch configuration: [--launch-arg VALUE ...] [--launch-env '{\"KEY\":\"VALUE\"}']\n Android: [--app-id ID] [--apk FILE] [--serial ID] [--keep-data]",
244
- init: "tapp init [repo] [--explore] [--refresh] [--platform PLATFORM] [--target NAME] [--url URL] [--dry-run]",
243
+ explore: "tapp explore [target] [--platform ios|android|web] [--actions N] [--timeout SEC] [--email VALUE] [--password VALUE] [--baseline FILE] [--json FILE]\n Web: [--watch] opens Tapp's controlled browser and shows its actions\n iOS launch configuration: [--launch-arg VALUE ...] [--launch-env '{\"KEY\":\"VALUE\"}']\n Android: [--app-id ID] [--apk FILE] [--serial ID] [--keep-data]",
244
+ init: "tapp init [repo] [--explore] [--refresh] [--platform PLATFORM] [--target NAME] [--url URL] [--watch] [--dry-run]",
245
245
  open: "tapp open [target] [--platform ios|android|web] [--out FILE] [--tap TEXT] [--wait-for TEXT]",
246
246
  tree: "tapp tree [target] [--platform ios|android|web] [--json] [--tap TEXT] [--wait-for TEXT]",
247
247
  shot: "tapp shot [--out FILE]",
@@ -346,6 +346,7 @@ switch (command) {
346
346
  timeout,
347
347
  testEmail: typeof flags.email === "string" ? flags.email : undefined,
348
348
  testPassword: typeof flags.password === "string" ? flags.password : undefined,
349
+ watch: flags.watch === true,
349
350
  runExploration: engine?.runInitExploration,
350
351
  onProgress: (progress) => {
351
352
  const activePlatform = progress.platform || platform;
@@ -387,17 +388,29 @@ switch (command) {
387
388
  if (typeof flags["json-out"] === "string") {
388
389
  const out = path.resolve(flags["json-out"]);
389
390
  fs.mkdirSync(path.dirname(out), { recursive: true });
390
- fs.writeFileSync(out, JSON.stringify({ model: built.model, plan: written?.plan || built.plan, ...(exploration ? { exploration } : {}) }, null, 2) + "\n");
391
- }
392
- const blocking = built.model.requirements.filter((item) => item.severity === "blocking");
391
+ fs.writeFileSync(out, JSON.stringify({
392
+ model: built.model,
393
+ plan: written?.plan || built.plan,
394
+ ...(exploration ? { exploration } : {}),
395
+ ...(result.selectedTarget ? { selectedTarget: result.selectedTarget } : {}),
396
+ requirementScope: result.requirementScope,
397
+ }, null, 2) + "\n");
398
+ }
399
+ const activeRequirements = result.requirementScope?.active || built.model.requirements;
400
+ const deferredRequirements = result.requirementScope?.deferred || [];
401
+ const blocking = activeRequirements.filter((item) => item.severity === "blocking");
402
+ const deferredBlocking = deferredRequirements.filter((item) => item.severity === "blocking");
393
403
  const pending = (written?.plan || built.plan).items.filter((item) => item.decision === "pending");
394
404
  console.log(`🧭 Tapp init — ${built.model.application.name}`);
395
405
  console.log(` targets: ${built.model.targets.length ? built.model.targets.map((target) => `${target.platform}:${target.name}`).join(", ") : "none"}`);
396
406
  console.log(` UI Map: ${built.model.uiMap.status} · ${built.model.uiMap.nodeCount} states · ${built.model.uiMap.edgeCount} transitions`);
397
407
  if (exploration) console.log(` Exploration: ${(exploration.findings || []).length} finding(s)${exploration.inconclusive ? " (inconclusive)" : ""} · ${exploration.uiMap.nodeCount} states · evidence: ${exploration.reportHtml || exploration.capture?.path || "capture recorded"}`);
398
408
  if (exploration?.managedRuntime) console.log(` Managed web runtime: built/started ${exploration.target} for exploration and stopped it afterward · log: ${exploration.runtime.logPath}`);
399
- console.log(` release plan: ${(written?.plan || built.plan).items.length} item(s) · ${pending.length} pending review · ${blocking.length} blocking requirement(s)`);
400
- for (const requirement of built.model.requirements) console.log(` ${requirement.severity === "blocking" ? "❌" : "⚠️"} ${requirement.message} Next: ${requirement.remediation}`);
409
+ const selectedLabel = result.selectedTarget ? ` for ${result.selectedTarget.platform}:${result.selectedTarget.name}` : "";
410
+ const deferredLabel = deferredBlocking.length ? ` · ${deferredBlocking.length} setup gap(s) on unselected target(s)` : "";
411
+ console.log(` release plan: ${(written?.plan || built.plan).items.length} item(s) · ${pending.length} pending review · ${blocking.length} blocking requirement(s)${selectedLabel}${deferredLabel}`);
412
+ for (const requirement of activeRequirements) console.log(` ${requirement.severity === "blocking" ? "❌" : "⚠️"} ${requirement.message} Next: ${requirement.remediation}`);
413
+ for (const requirement of deferredRequirements) console.log(` ℹ️ Unselected ${requirement.targetPlatform}:${requirement.targetName} setup gap: ${requirement.message} Next: ${requirement.remediation}`);
401
414
  if (written) console.log(` model: ${written.modelPath}\n plan: ${written.planPath}`);
402
415
  else console.log(" dry run: repository files were not changed");
403
416
  break;
@@ -545,6 +558,7 @@ switch (command) {
545
558
  testPassword: flags.password,
546
559
  ...launchOptions,
547
560
  baselineFindings,
561
+ watch: flags.watch === true,
548
562
  surface: "cli",
549
563
  onProgress,
550
564
  onStatus: (t) => console.error(`ℹ️ ${t}`),
@@ -565,6 +579,10 @@ switch (command) {
565
579
  process.exit(2);
566
580
  }
567
581
  if (platform === "ios") requireMacFor("iOS testing");
582
+ if (flags.watch === true && platform !== "web") {
583
+ console.error("❌ --watch is currently available for web exploration only");
584
+ process.exit(2);
585
+ }
568
586
  if (platform !== "ios" && Object.keys(launchOptions).length) {
569
587
  console.error("❌ --launch-arg and --launch-env apply only to iOS targets");
570
588
  process.exit(2);
@@ -586,6 +604,7 @@ switch (command) {
586
604
  testEmail: flags.email,
587
605
  testPassword: flags.password,
588
606
  baselineFindings,
607
+ watch: flags.watch === true,
589
608
  surface: "cli",
590
609
  onProgress,
591
610
  })
@@ -1557,7 +1576,7 @@ switch (command) {
1557
1576
  Core — explore, prove, gate (agents and humans can just run these — no server, no setup):
1558
1577
  tapp explore [target] Autonomous exploration → findings + evidence (an observation, NOT a
1559
1578
  release decision — run 'tapp ci' to gate a merge)
1560
- (--platform ios|android|web · --app-id ID · --apk FILE · --actions N)
1579
+ (web: --watch · all: --platform ios|android|web · --actions N)
1561
1580
  tapp contract run FILE Replay a business-level release contract — the guarantees that must hold
1562
1581
  tapp ci ... Merge-blocking release gate — explore + suites + baseline → pass/fail/inconclusive
1563
1582
  (see: tapp ci --help)
@@ -1617,6 +1636,11 @@ Setup:
1617
1636
  tapp doctor Check Xcode / simulators / toolchain
1618
1637
  tapp mcp Start the MCP server on stdio (adds inline screenshots + interactive sessions)
1619
1638
 
1639
+ Agent Skill (optional — so a short “Use Tapp to test this app” prompt is enough):
1640
+ Any supported agent: npx -y skills add aarwitz/tapp --skill tapp
1641
+ Claude skill + MCP: claude plugin marketplace add aarwitz/tapp
1642
+ claude plugin install tapp@tapp
1643
+
1620
1644
  MCP hookup (optional — for inline screenshots and the tap/type/inspect session loop):
1621
1645
  Claude Code: claude mcp add tapp -- npx -y @aarwitz/tapp mcp
1622
1646
  Cursor/VS Code (mcp.json):
@@ -16,12 +16,14 @@ resolution actually builds and installs the detected Xcode container, the model
16
16
  scheme as runtime-observed validation and removes the corresponding confirmation blocker. Merely
17
17
  supplying a bundle id or prebuilt `.app` does not prove repository build configuration.
18
18
 
19
- A fresh repository with more than one detected application target is never resolved by detection
20
- order. Bare `tapp init . --explore` prompts in a human TTY; non-interactive CLI/MCP callers receive
19
+ A repository with more than one detected application target is never resolved by detection order
20
+ during explicit initialization, even when a prior default exists. Bare `tapp init . --explore`
21
+ prompts in a human TTY; non-interactive CLI/MCP callers receive
21
22
  exact `--platform`/`--target` commands before any build or write, and MCP also carries them as
22
23
  structured `target-selection-required` choices. The selected run records that target as the default
23
- for later bare exploration, but the application model retains the repository's other detected targets
24
- and their unmet coverage.
24
+ for later bare `tapp explore`, but the application model retains the repository's other detected
25
+ targets and their unmet coverage. The selected init run reports other-target setup gaps as deferred
26
+ information rather than presenting them as failures of the target that was actually explored.
25
27
 
26
28
  ## First inspection
27
29