@aarwitz/tapp 0.15.0 → 0.15.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.
@@ -8,7 +8,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
8
8
  let vc = UIViewController()
9
9
  vc.view.backgroundColor = .black
10
10
  let label = UILabel()
11
- label.text = "AutoTap Harness"
11
+ label.text = "Tapp Harness"
12
12
  label.textColor = .white
13
13
  label.textAlignment = .center
14
14
  label.frame = vc.view.bounds
@@ -166,7 +166,7 @@
166
166
  CODE_SIGN_STYLE = Automatic;
167
167
  DEVELOPMENT_TEAM = "";
168
168
  INFOPLIST_FILE = OCQAHarness/Info.plist;
169
- PRODUCT_BUNDLE_IDENTIFIER = ai.autotap.harness;
169
+ PRODUCT_BUNDLE_IDENTIFIER = io.github.aarwitz.tapp.harness;
170
170
  PRODUCT_NAME = "$(TARGET_NAME)";
171
171
  SWIFT_VERSION = 5.0;
172
172
  TARGETED_DEVICE_FAMILY = "1,2";
@@ -186,7 +186,7 @@
186
186
  CODE_SIGN_STYLE = Automatic;
187
187
  DEVELOPMENT_TEAM = "";
188
188
  INFOPLIST_FILE = OCQAHarnessUITests/Info.plist;
189
- PRODUCT_BUNDLE_IDENTIFIER = ai.autotap.harness.uitests;
189
+ PRODUCT_BUNDLE_IDENTIFIER = io.github.aarwitz.tapp.harness.uitests;
190
190
  PRODUCT_NAME = "$(TARGET_NAME)";
191
191
  SWIFT_VERSION = 5.0;
192
192
  TARGETED_DEVICE_FAMILY = "1,2";
@@ -670,7 +670,7 @@ class ExplorerTests: XCTestCase {
670
670
 
671
671
  /// Replays an explicit, config-driven login flow (OCQA_LOGIN_STEPS) before exploration — for the
672
672
  /// custom login UIs the heuristic preamble can't parse, which are the #1 reason a real app stays
673
- /// invisible to AutoTap. Steps are a JSON array of {action: type|tap|wait, target: <id-or-label>,
673
+ /// invisible to Tapp. Steps are a JSON array of {action: type|tap|wait, target: <id-or-label>,
674
674
  /// value?: <text; "$TEST_EMAIL"/"$TEST_PASSWORD" substituted from stored creds>, timeoutMs?}.
675
675
  /// Returns true if any step ran (so the caller skips the heuristic preamble). Tolerant: a failed
676
676
  /// step is logged but doesn't abort — exploration still proceeds, and the coverage eval reveals
@@ -1320,7 +1320,7 @@ class ExplorerTests: XCTestCase {
1320
1320
  // so it surfaces here at the next tree read rather than at the early crash check.
1321
1321
  // Report it against the action that led here instead of breaking silently. (Real:
1322
1322
  // Wikipedia's WMFCaptchaViewController.refreshImage assertionFailure crashes the
1323
- // app during login — a genuine production crash AutoTap must surface.)
1323
+ // app during login — a genuine production crash Tapp must surface.)
1324
1324
  if app.state != .runningForeground {
1325
1325
  let where_ = pendingTransitionFrom?.title ?? "the previous screen"
1326
1326
  let crashKey = "crash-async:\(where_)"
@@ -182,7 +182,7 @@ pbxproj = <<~PBX
182
182
  CODE_SIGN_STYLE = Automatic;
183
183
  DEVELOPMENT_TEAM = "";
184
184
  INFOPLIST_FILE = OCQAHarness/Info.plist;
185
- PRODUCT_BUNDLE_IDENTIFIER = ai.autotap.harness;
185
+ PRODUCT_BUNDLE_IDENTIFIER = io.github.aarwitz.tapp.harness;
186
186
  PRODUCT_NAME = "$(TARGET_NAME)";
187
187
  SWIFT_VERSION = 5.0;
188
188
  TARGETED_DEVICE_FAMILY = "1,2";
@@ -202,7 +202,7 @@ pbxproj = <<~PBX
202
202
  CODE_SIGN_STYLE = Automatic;
203
203
  DEVELOPMENT_TEAM = "";
204
204
  INFOPLIST_FILE = OCQAHarnessUITests/Info.plist;
205
- PRODUCT_BUNDLE_IDENTIFIER = ai.autotap.harness.uitests;
205
+ PRODUCT_BUNDLE_IDENTIFIER = io.github.aarwitz.tapp.harness.uitests;
206
206
  PRODUCT_NAME = "$(TARGET_NAME)";
207
207
  SWIFT_VERSION = 5.0;
208
208
  TARGETED_DEVICE_FAMILY = "1,2";
@@ -249,6 +249,6 @@ SCHEME
249
249
  File.write("#{scheme_dir}/OCQAHarnessUITests.xcscheme", scheme)
250
250
 
251
251
  puts "✅ Generated OCQAHarness.xcodeproj"
252
- puts " Host app: ai.autotap.harness"
252
+ puts " Host app: io.github.aarwitz.tapp.harness"
253
253
  puts " Test target: OCQAHarnessUITests"
254
254
  puts " Platform: iOS 16.0+"
package/README.md CHANGED
@@ -65,8 +65,7 @@ npx -y @aarwitz/tapp qa # finds your Xcode project → builds → installs on
65
65
  ```
66
66
 
67
67
  The product, executable, and package leaf are all Tapp: npm distributes it as
68
- `@aarwitz/tapp`, while the installed command remains `tapp`. Existing `npx runtapp ...`
69
- and `npx tapp-mcp ...` configurations remain supported as deprecated compatibility paths.
68
+ `@aarwitz/tapp`, while the installed command remains `tapp`.
70
69
 
71
70
  To bootstrap maintained release infrastructure, preview the repository model and grounded plan
72
71
  before Tapp writes anything:
@@ -292,7 +291,7 @@ npx -y @aarwitz/tapp ci --app path/to/MyApp.app \
292
291
  ```
293
292
 
294
293
  See the self-test at
295
- [.github/workflows/autotap-gate-example.yml](.github/workflows/autotap-gate-example.yml) for
294
+ [.github/workflows/tapp-gate-example.yml](.github/workflows/tapp-gate-example.yml) for
296
295
  Flows, auth inputs, and other controls. GitHub-hosted iOS runs require a macOS runner; the first
297
296
  run also builds the XCUITest harness, so budget roughly 5–10 minutes depending on app size.
298
297
 
@@ -317,8 +316,8 @@ environment. Add
317
316
  [`docs/scenarios.md`](docs/scenarios.md). Automatic
318
317
  baselines are isolated by platform and target, so two same-platform apps are never compared.
319
318
 
320
- **The hosted service at `app.runtapp.com` is under development and is not currently offered for
321
- customer repositories.** Do not upload private code or credentials to an old preview. The retained
319
+ **The hosted service is under development and is not currently offered for customer repositories.**
320
+ Do not upload private code or credentials to an old preview. The retained
322
321
  cloud prototype is not the production SaaS boundary. Use the local Release Studio and the portable
323
322
  GitHub Action in infrastructure you control until the new account, tenant authorization, private
324
323
  evidence, and isolated-worker boundary passes security review.
@@ -352,8 +351,8 @@ verdict calculation run entirely locally — no telemetry, nothing phones home.
352
351
  features are explicit: finding enrichment requires `TAPP_ENABLE_REMOTE_AI=1` (an ambient
353
352
  API key alone never changes data handling), and AI flow generation / `assert_ai` only run
354
353
  when you invoke them; these send selected metadata (screen names, finding titles) to your
355
- configured model provider. Env vars: `TAPP_*` preferred; `AUTOTAP_*` accepted as deprecated
356
- aliases.
354
+ configured model provider. Runtime configuration uses `TAPP_*` environment variables; deprecated
355
+ aliases remain readable for compatibility.
357
356
 
358
357
  Committed Flow replay, recording a driven session, autonomous exploration, exact assertions,
359
358
  regression comparison, and CI gating require **no API key and no coding agent at runtime**. AI is
package/bin/tapp.js CHANGED
@@ -16,7 +16,6 @@
16
16
  //
17
17
  // All writable output (captures, harness build cache) goes to ~/.tapp (override
18
18
  // with TAPP_HOME). The package directory itself is never written to.
19
- // (Internally exported as AUTOTAP_HOME — the env name the bundled scripts read.)
20
19
 
21
20
  import { spawnSync, spawn } from "node:child_process";
22
21
  import fs from "node:fs";
@@ -29,10 +28,11 @@ const packageRoot = path.resolve(__dirname, "..");
29
28
  const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
30
29
 
31
30
  // Redirect all writable output away from the (possibly read-only) package dir.
32
- if (!process.env.AUTOTAP_HOME) {
33
- process.env.AUTOTAP_HOME = process.env.TAPP_HOME || path.join(os.homedir(), ".tapp");
34
- }
35
- fs.mkdirSync(process.env.AUTOTAP_HOME, { recursive: true });
31
+ // The old environment alias remains populated only for older helper integrations.
32
+ const tappHome = (process.env.TAPP_HOME || process.env.AUTOTAP_HOME || path.join(os.homedir(), ".tapp")).trim();
33
+ process.env.TAPP_HOME = tappHome;
34
+ if (!process.env.AUTOTAP_HOME) process.env.AUTOTAP_HOME = tappHome;
35
+ fs.mkdirSync(tappHome, { recursive: true });
36
36
 
37
37
  const [, , command = "help", ...rest] = process.argv;
38
38
 
@@ -88,7 +88,7 @@ function bootBestSimulator(preferredName = "iPhone 16 Pro") {
88
88
  }
89
89
 
90
90
  function harnessXctestrun() {
91
- const dir = path.join(process.env.AUTOTAP_HOME, "harness-derived", "Build", "Products");
91
+ const dir = path.join(tappHome, "harness-derived", "Build", "Products");
92
92
  try {
93
93
  const found = fs.readdirSync(dir).find((f) => f.endsWith(".xctestrun"));
94
94
  return found ? path.join(dir, found) : null;
@@ -171,7 +171,7 @@ function androidTarget(flags, target = "") {
171
171
  }
172
172
 
173
173
  function saveShot(img, outFlag, name) {
174
- const out = outFlag || path.join(process.env.AUTOTAP_HOME, "shots", name);
174
+ const out = outFlag || path.join(tappHome, "shots", name);
175
175
  fs.mkdirSync(path.dirname(out), { recursive: true });
176
176
  fs.writeFileSync(out, Buffer.from(img.data, "base64"));
177
177
  return out;
@@ -472,7 +472,7 @@ switch (command) {
472
472
  await driver.forceStop();
473
473
  console.log(`🚀 Launched \`${target.appId}\` (Android)\n`);
474
474
  console.log(engine.formatScreen(snap.screenTitle, snap.elements));
475
- const out = typeof flags.out === "string" ? flags.out : path.join(process.env.AUTOTAP_HOME, "shots", `${target.appId}-${Date.now()}.png`);
475
+ const out = typeof flags.out === "string" ? flags.out : path.join(tappHome, "shots", `${target.appId}-${Date.now()}.png`);
476
476
  fs.mkdirSync(path.dirname(out), { recursive: true });
477
477
  fs.writeFileSync(out, data);
478
478
  console.log(`\n📸 Screenshot: ${out}`);
@@ -663,7 +663,7 @@ switch (command) {
663
663
  url: typeof flags.url === "string" ? flags.url : task.url || "",
664
664
  reset: task.reset || "launch", vars: compiled.vars, steps: compiled.steps, taskPlan: compiled.plan,
665
665
  };
666
- const out = path.resolve(typeof flags.out === "string" ? flags.out : path.join(process.env.AUTOTAP_HOME, "tasks", `${task.name}-${process.pid}-${Date.now()}.json`));
666
+ const out = path.resolve(typeof flags.out === "string" ? flags.out : path.join(tappHome, "tasks", `${task.name}-${process.pid}-${Date.now()}.json`));
667
667
  fs.mkdirSync(path.dirname(out), { recursive: true });
668
668
  fs.writeFileSync(out, JSON.stringify(flow, null, 2) + "\n");
669
669
  if (verb === "compile") {
@@ -712,8 +712,8 @@ switch (command) {
712
712
  break;
713
713
  }
714
714
  const token = `${process.pid}-${Date.now()}`;
715
- const flowLog = path.join(process.env.AUTOTAP_HOME, "flows", `${token}.log`);
716
- const evidenceDir = path.join(process.env.AUTOTAP_HOME, "captures", `flow-${platform}-${token}`);
715
+ const flowLog = path.join(tappHome, "flows", `${token}.log`);
716
+ const evidenceDir = path.join(tappHome, "captures", `flow-${platform}-${token}`);
717
717
  fs.mkdirSync(path.dirname(flowLog), { recursive: true });
718
718
  const env = { ...process.env, FLOW_LOG: flowLog, TAPP_FLOW_EVIDENCE_DIR: evidenceDir };
719
719
  if (typeof flags.email === "string") env.OCQA_TEST_EMAIL = flags.email;
@@ -779,7 +779,7 @@ switch (command) {
779
779
  let compiled;
780
780
  try { compiled = compileReleaseContract(contract, { platform, sourcePath: contractPath }); }
781
781
  catch (error) { console.error(`❌ Could not compile Release Contract: ${error.message}`); process.exit(2); }
782
- const out = path.resolve(typeof flags.out === "string" ? flags.out : path.join(process.env.AUTOTAP_HOME, "contracts", `${contract.name}-${process.pid}-${Date.now()}.json`));
782
+ const out = path.resolve(typeof flags.out === "string" ? flags.out : path.join(tappHome, "contracts", `${contract.name}-${process.pid}-${Date.now()}.json`));
783
783
  fs.mkdirSync(path.dirname(out), { recursive: true });
784
784
  fs.writeFileSync(out, JSON.stringify(compiled, null, 2) + "\n");
785
785
  if (verb === "compile") {
@@ -893,8 +893,8 @@ switch (command) {
893
893
  break;
894
894
  }
895
895
  const token = `${process.pid}-${Date.now()}`;
896
- const flowLog = path.join(process.env.AUTOTAP_HOME, "scenarios", `${token}.log`);
897
- const evidenceDir = path.join(process.env.AUTOTAP_HOME, "captures", `scenario-web-${token}`);
896
+ const flowLog = path.join(tappHome, "scenarios", `${token}.log`);
897
+ const evidenceDir = path.join(tappHome, "captures", `scenario-web-${token}`);
898
898
  const env = { ...process.env, FLOW_LOG: flowLog, TAPP_FLOW_EVIDENCE_DIR: evidenceDir };
899
899
  const url = typeof flags.url === "string" ? flags.url : scenario.url || scenario.app || "";
900
900
  const result = spawnSync(process.execPath, [path.join(packageRoot, "scripts", "run-web-scenario.js"), absolute, url], { stdio: "inherit", env });
@@ -1007,7 +1007,7 @@ switch (command) {
1007
1007
  console.log(" ⬜ Web — install Playwright in the app workspace: npm install -D playwright && npx playwright install chromium");
1008
1008
  }
1009
1009
 
1010
- console.log(`\n Home: ${process.env.AUTOTAP_HOME}`);
1010
+ console.log(`\n Home: ${tappHome}`);
1011
1011
  console.log(healthy ? "\nReady. Add to your agent: claude mcp add tapp -- npx -y @aarwitz/tapp mcp" : "\nFix the ❌ items above, then re-run: tapp doctor");
1012
1012
  process.exit(healthy ? 0 : 1);
1013
1013
  }
@@ -1284,7 +1284,7 @@ switch (command) {
1284
1284
 
1285
1285
  case "report": {
1286
1286
  // Regenerate + open the HTML evidence page for a capture (default: the latest).
1287
- const capturesDir = path.join(process.env.AUTOTAP_HOME, "captures");
1287
+ const capturesDir = path.join(tappHome, "captures");
1288
1288
  const repoCaptures = path.join(packageRoot, "captures");
1289
1289
  const roots = [capturesDir, repoCaptures].filter((d) => fs.existsSync(d));
1290
1290
  const runs = roots
@@ -62,7 +62,7 @@ intent.
62
62
 
63
63
  ## Hosted relationship
64
64
 
65
- `app.runtapp.com` will present the same product journey through a different adapter: application
65
+ The future hosted application will present the same product journey through a different adapter: application
66
66
  accounts/organizations, GitHub App repository authorization, private storage, a durable queue, and
67
67
  isolated managed workers. It cannot reuse the loopback session, local `gh` authority, filesystem
68
68
  boundary, or in-memory ownership assumptions.
@@ -184,12 +184,7 @@ plan while preserving fields from newer engine versions; committed contract inte
184
184
  through these proposal controls. Flow Map merges the repository `.autotap/ui-map.json` with current
185
185
  run evidence instead of building a separate desktop-only graph.
186
186
 
187
- Schema compatibility can be rehearsed headlessly after building the desktop app:
188
-
189
- ```bash
190
- AutoTap.app/Contents/MacOS/AutoTap --verify-artifacts /path/to/repository \
191
- --out /tmp/tapp-desktop-artifacts.json
192
- ```
187
+ Schema compatibility is exercised by the repository's protocol tests and retained desktop reader.
193
188
 
194
189
  `plan generate` handles only explicitly approved proposals. Grounded cross-actor proposals preserve
195
190
  actor-attributed Task calls, captured output variables, bounded eventual assertions, and the
@@ -109,7 +109,7 @@ function assetPath(pathname) {
109
109
  }
110
110
 
111
111
  function captureRoot() {
112
- return path.join(process.env.AUTOTAP_HOME || process.env.TAPP_HOME || path.join(os.homedir(), ".tapp"), "captures");
112
+ return path.join(process.env.TAPP_HOME || process.env.AUTOTAP_HOME || path.join(os.homedir(), ".tapp"), "captures");
113
113
  }
114
114
 
115
115
  function capturePath(captureId, relative = "report.html") {
@@ -40,7 +40,7 @@ export async function enrichFindings(findings, { backend, callModel, screens = [
40
40
  `Title: ${f.title}`;
41
41
  try {
42
42
  const res = await Promise.race([
43
- callModel(backend, { system, userText, model: process.env.AUTOTAP_FINDING_MODEL || "claude-haiku-4-5-20251001", maxTokens: 300 }),
43
+ callModel(backend, { system, userText, model: process.env.TAPP_FINDING_MODEL || process.env.AUTOTAP_FINDING_MODEL || "claude-haiku-4-5-20251001", maxTokens: 300 }),
44
44
  new Promise((r) => setTimeout(() => r({ error: "timeout" }), TIMEOUT_MS)),
45
45
  ]);
46
46
  const parsed = res && !res.error ? parseEnrichment(res.text) : null;
@@ -18,10 +18,10 @@ const __filename = fileURLToPath(import.meta.url);
18
18
  const __dirname = path.dirname(__filename);
19
19
  const repoRoot = path.resolve(__dirname, "../..");
20
20
  const scriptsDir = path.join(repoRoot, "scripts");
21
- // AUTOTAP_HOME (set by the `tapp` CLI when running as an installed npm package) redirects
22
- // all writable output to a user dir; unset (repo dev flow) captures stay in the repo.
23
- const autotapHome = (process.env.AUTOTAP_HOME || "").trim();
24
- const capturesDir = autotapHome ? path.join(autotapHome, "captures") : path.join(repoRoot, "captures");
21
+ // TAPP_HOME (set by the `tapp` CLI when installed) redirects writable output to a user directory.
22
+ // The old alias remains a read-only fallback; unset repository development stays local.
23
+ const tappHome = (process.env.TAPP_HOME || process.env.AUTOTAP_HOME || "").trim();
24
+ const capturesDir = tappHome ? path.join(tappHome, "captures") : path.join(repoRoot, "captures");
25
25
  const MAX_OUTPUT_CHARS = 60_000;
26
26
  const requiredAuthToken = (process.env.TAPP_MCP_TOKEN || process.env.AUTOTAP_MCP_TOKEN || "").trim();
27
27
 
@@ -83,7 +83,7 @@ function ensureAuthorized(args = {}) {
83
83
  const provided = typeof args.authToken === "string" ? args.authToken.trim() : "";
84
84
  if (provided !== requiredAuthToken) {
85
85
  return errorResult("Unauthorized", {
86
- reason: "Provide valid authToken when AUTOTAP_MCP_TOKEN is set",
86
+ reason: "Provide valid authToken when TAPP_MCP_TOKEN is set",
87
87
  });
88
88
  }
89
89
 
@@ -339,7 +339,7 @@ export async function buildAppForSim({ dir, container, scheme, configuration = "
339
339
  };
340
340
  }
341
341
  }
342
- const derived = path.join(autotapHome || os.tmpdir(), "app-builds", schemeName.replace(/[^a-zA-Z0-9]/g, "_"));
342
+ const derived = path.join(tappHome || os.tmpdir(), "app-builds", schemeName.replace(/[^a-zA-Z0-9]/g, "_"));
343
343
  const build = await runCommand(
344
344
  "xcodebuild",
345
345
  [
@@ -1065,7 +1065,7 @@ export async function captureScreenshotImage(maxWidth) {
1065
1065
  // change data-handling behavior. A subscription token is an explicit tapp choice, and
1066
1066
  // explicitly-invoked AI tools (tapp_flow_generate, assert_ai) carry their own consent.
1067
1067
  export function remoteAiOptedIn(env = process.env) {
1068
- if ((env.AUTOTAP_SUBSCRIPTION_TOKEN || env.TAPP_SUBSCRIPTION_TOKEN || "").trim()) return true;
1068
+ if ((env.TAPP_SUBSCRIPTION_TOKEN || env.AUTOTAP_SUBSCRIPTION_TOKEN || "").trim()) return true;
1069
1069
  return ["1", "true", "yes"].includes(String(env.TAPP_ENABLE_REMOTE_AI || "").trim().toLowerCase());
1070
1070
  }
1071
1071
 
@@ -1077,9 +1077,9 @@ export function isInsideDir(root, p) {
1077
1077
  }
1078
1078
 
1079
1079
  function resolveModelBackend() {
1080
- const token = (process.env.AUTOTAP_SUBSCRIPTION_TOKEN || "").trim();
1080
+ const token = (process.env.TAPP_SUBSCRIPTION_TOKEN || process.env.AUTOTAP_SUBSCRIPTION_TOKEN || "").trim();
1081
1081
  if (token) {
1082
- const base = (process.env.AUTOTAP_PROXY_URL || "http://localhost:8787").replace(/\/$/, "");
1082
+ const base = (process.env.TAPP_PROXY_URL || process.env.AUTOTAP_PROXY_URL || "http://localhost:8787").replace(/\/$/, "");
1083
1083
  const url = base.endsWith("/v1/messages") ? base : base + "/v1/messages";
1084
1084
  return { url, headers: { authorization: `Bearer ${token}`, "content-type": "application/json" } };
1085
1085
  }
@@ -1091,7 +1091,7 @@ function resolveModelBackend() {
1091
1091
  }
1092
1092
 
1093
1093
  async function callModel(backend, { system, userText, model, maxTokens = 1500 }) {
1094
- const body = JSON.stringify({ model: model || process.env.AUTOTAP_FLOW_MODEL || "claude-sonnet-4-6", max_tokens: maxTokens, system, messages: [{ role: "user", content: userText }] });
1094
+ const body = JSON.stringify({ model: model || process.env.TAPP_FLOW_MODEL || process.env.AUTOTAP_FLOW_MODEL || "claude-sonnet-4-6", max_tokens: maxTokens, system, messages: [{ role: "user", content: userText }] });
1095
1095
  const res = await fetch(backend.url, { method: "POST", headers: backend.headers, body });
1096
1096
  if (!res.ok) return { error: `model HTTP ${res.status}: ${(await res.text()).slice(0, 300)}` };
1097
1097
  const data = await res.json();
@@ -1832,7 +1832,7 @@ export async function startManagedWebTarget({ root, requestedTarget = "", timeou
1832
1832
  command = process.execPath;
1833
1833
  startArgs = [path.join(__dirname, "static-server.js"), startDir, String(port)];
1834
1834
  }
1835
- const logDir = path.join(autotapHome || os.tmpdir(), "init-runtime");
1835
+ const logDir = path.join(tappHome || os.tmpdir(), "init-runtime");
1836
1836
  fs.mkdirSync(logDir, { recursive: true });
1837
1837
  const logPath = path.join(logDir, `web-${process.pid}-${Date.now()}.log`);
1838
1838
  const child = spawn(command, startArgs, {
@@ -1938,7 +1938,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1938
1938
  properties: {
1939
1939
  authToken: {
1940
1940
  type: "string",
1941
- description: "Required when AUTOTAP_MCP_TOKEN is set",
1941
+ description: "Required when TAPP_MCP_TOKEN is set",
1942
1942
  },
1943
1943
  projectDir: { type: "string", description: "Repo/dir to search for the .xcworkspace/.xcodeproj (default: cwd)" },
1944
1944
  scheme: { type: "string", description: "Scheme to build (default: auto-detected)" },
@@ -1956,7 +1956,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1956
1956
  properties: {
1957
1957
  authToken: {
1958
1958
  type: "string",
1959
- description: "Required when AUTOTAP_MCP_TOKEN is set",
1959
+ description: "Required when TAPP_MCP_TOKEN is set",
1960
1960
  },
1961
1961
  mode: {
1962
1962
  type: "string",
@@ -2075,7 +2075,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2075
2075
  inputSchema: {
2076
2076
  type: "object",
2077
2077
  properties: {
2078
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2078
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2079
2079
  appBundleId: { type: "string", description: "iOS: bundle id of the installed app to test, e.g. com.acme.app. Provide exactly one of appBundleId | url." },
2080
2080
  androidAppId: { type: "string", description: "Android: application id installed on a connected emulator/device, e.g. com.acme.app." },
2081
2081
  apkPath: { type: "string", description: "Android: optional APK to install before testing." },
@@ -2137,7 +2137,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2137
2137
  inputSchema: {
2138
2138
  type: "object",
2139
2139
  properties: {
2140
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2140
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2141
2141
  operation: { type: "string", enum: ["inspect", "write", "refresh", "explore"], default: "inspect" },
2142
2142
  projectDir: { type: "string", description: "Repo-relative project root; defaults to the MCP workspace root" },
2143
2143
  platform: { type: "string", enum: ["ios", "android", "web"], description: "Optional target filter" },
@@ -2164,7 +2164,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2164
2164
  inputSchema: {
2165
2165
  type: "object",
2166
2166
  properties: {
2167
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2167
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2168
2168
  operation: { type: "string", enum: ["read", "set"], default: "read" },
2169
2169
  projectDir: { type: "string", description: "Repo-relative project root; defaults to the MCP workspace root" },
2170
2170
  name: { type: "string", description: "Set: stable actor name" },
@@ -2184,7 +2184,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2184
2184
  inputSchema: {
2185
2185
  type: "object",
2186
2186
  properties: {
2187
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2187
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2188
2188
  operation: { type: "string", enum: ["read", "review", "generate", "validate", "promote"], default: "read" },
2189
2189
  planPath: { type: "string", description: "Repo-relative plan path; default .autotap/release-plan.json" },
2190
2190
  projectDir: { type: "string", description: "Generate: repo-relative project root containing the scoped .autotap Task directories" },
@@ -2211,7 +2211,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2211
2211
  inputSchema: {
2212
2212
  type: "object",
2213
2213
  properties: {
2214
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2214
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2215
2215
  operation: { type: "string", enum: ["inspect", "install", "baseline"], default: "inspect" },
2216
2216
  projectDir: { type: "string", description: "Repo-relative project root; defaults to the MCP workspace root" },
2217
2217
  modelPath: { type: "string", description: "Repo-relative application model path; defaults to <projectDir>/.autotap/application-model.json" },
@@ -2237,7 +2237,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2237
2237
  inputSchema: {
2238
2238
  type: "object",
2239
2239
  properties: {
2240
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2240
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2241
2241
  operation: { type: "string", enum: ["read", "build", "diff"], default: "read" },
2242
2242
  captureId: { type: "string", description: "Read/build from this Tapp capture's ocqa-markers.txt/ui-map.json" },
2243
2243
  mapPath: { type: "string", description: "Repo-relative UI Map path for read, or build output (default .autotap/ui-map.json)" },
@@ -2261,7 +2261,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2261
2261
  type: "object",
2262
2262
  required: ["taskPath"],
2263
2263
  properties: {
2264
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2264
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2265
2265
  operation: { type: "string", enum: ["read", "validate", "compile"], default: "validate" },
2266
2266
  taskPath: { type: "string", description: "Repo-relative .autotap/tasks/*.yml|json file" },
2267
2267
  platform: { type: "string", enum: ["ios", "android", "web"], description: "Implementation to validate/compile" },
@@ -2282,7 +2282,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2282
2282
  type: "object",
2283
2283
  required: ["contractPath"],
2284
2284
  properties: {
2285
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2285
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2286
2286
  operation: { type: "string", enum: ["read", "validate", "compile", "run"], default: "validate" },
2287
2287
  contractPath: { type: "string", description: "Repo-relative .autotap/contracts/*.contract.ts file" },
2288
2288
  platform: { type: "string", enum: ["ios", "android", "web"], description: "Target platform; optional when the contract declares exactly one" },
@@ -2305,7 +2305,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2305
2305
  inputSchema: {
2306
2306
  type: "object",
2307
2307
  properties: {
2308
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2308
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2309
2309
  operation: { type: "string", enum: ["plan", "adopt"], default: "plan" },
2310
2310
  changedFiles: {
2311
2311
  type: "array", minItems: 1,
@@ -2350,7 +2350,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2350
2350
  inputSchema: {
2351
2351
  type: "object",
2352
2352
  properties: {
2353
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2353
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2354
2354
  flow: {
2355
2355
  type: "object",
2356
2356
  description:
@@ -2379,7 +2379,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2379
2379
  inputSchema: {
2380
2380
  type: "object",
2381
2381
  properties: {
2382
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2382
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2383
2383
  scenario: { type: "object", description: "Inline Scenario with {kind:'scenario', platform:'web', actors, steps, setup?, teardown?}" },
2384
2384
  scenarioPath: { type: "string", description: "Repo-relative path to a .yml/.json Scenario" },
2385
2385
  url: { type: "string", description: "Override the Scenario's web URL" },
@@ -2400,7 +2400,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2400
2400
  inputSchema: {
2401
2401
  type: "object",
2402
2402
  properties: {
2403
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2403
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2404
2404
  goal: { type: "string", description: "What the test should do, in plain English (e.g. 'sign in with test creds and reach the dashboard')" },
2405
2405
  appBundleId: { type: "string", description: "Bundle id of the installed app to author against" },
2406
2406
  captureId: { type: "string", description: "Reuse this capture's grounding instead of exploring (from a prior run_qa, faster)" },
@@ -2424,7 +2424,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2424
2424
  inputSchema: {
2425
2425
  type: "object",
2426
2426
  properties: {
2427
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2427
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2428
2428
  name: { type: "string", description: "Human name for the flow, e.g. 'Sign in and reach Home'" },
2429
2429
  addFinalAssertion: { type: "boolean", default: true, description: "Append assert_screen for the final screen as a checkpoint" },
2430
2430
  replace: { type: "boolean", default: false, description: "Explicitly replace a Flow with the same generated filename. Existing Flows are preserved by default." },
@@ -2442,7 +2442,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2442
2442
  inputSchema: {
2443
2443
  type: "object",
2444
2444
  properties: {
2445
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2445
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2446
2446
  appBundleId: { type: "string", description: "iOS bundle id of the installed app" },
2447
2447
  androidAppId: { type: "string", description: "Android application id of the installed app" },
2448
2448
  androidSerial: { type: "string", description: "Android adb device serial" },
@@ -2459,7 +2459,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2459
2459
  inputSchema: {
2460
2460
  type: "object",
2461
2461
  properties: {
2462
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2462
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2463
2463
  maxWidth: { type: "integer", minimum: 200, maximum: 1400, default: 700, description: "Max image width in px (downscaled to keep payload small)" },
2464
2464
  },
2465
2465
  },
@@ -2477,7 +2477,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2477
2477
  inputSchema: {
2478
2478
  type: "object",
2479
2479
  properties: {
2480
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2480
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2481
2481
  appBundleId: { type: "string", description: "iOS bundle id of the installed app" },
2482
2482
  androidAppId: { type: "string", description: "Android application id of the installed app" },
2483
2483
  apkPath: { type: "string", description: "Android APK to install before launch" },
@@ -2502,7 +2502,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2502
2502
  inputSchema: {
2503
2503
  type: "object",
2504
2504
  properties: {
2505
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2505
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2506
2506
  udid: { type: "string", description: "Simulator UDID (from tapp_list_simulators)" },
2507
2507
  name: { type: "string", description: "Simulator name, e.g. 'iPhone 16 Pro' (used if udid omitted)" },
2508
2508
  },
@@ -2518,7 +2518,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2518
2518
  inputSchema: {
2519
2519
  type: "object",
2520
2520
  properties: {
2521
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2521
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2522
2522
  project: { type: "string", description: "Absolute path to .xcodeproj (use this OR workspace)" },
2523
2523
  workspace: { type: "string", description: "Absolute path to .xcworkspace (use this OR project)" },
2524
2524
  scheme: { type: "string", description: "Scheme to build" },
@@ -2542,7 +2542,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2542
2542
  inputSchema: {
2543
2543
  type: "object",
2544
2544
  properties: {
2545
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2545
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2546
2546
  appBundleId: { type: "string", description: "Bundle id of the installed app to drive" },
2547
2547
  androidAppId: { type: "string", description: "Android application id to drive (alternative to appBundleId)" },
2548
2548
  apkPath: { type: "string", description: "Android APK to install before starting" },
@@ -2571,7 +2571,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2571
2571
  inputSchema: {
2572
2572
  type: "object",
2573
2573
  properties: {
2574
- authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" },
2574
+ authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" },
2575
2575
  action: { type: "string", enum: ["login", "tap", "type", "swipe", "back", "wait", "tree", "screenshot"] },
2576
2576
  email: { type: "string", description: "login: email/username to sign in with" },
2577
2577
  password: { type: "string", description: "login: password to sign in with" },
@@ -2592,7 +2592,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2592
2592
  description: "End the active interactive session (quits the app + harness). Always call this when done.",
2593
2593
  inputSchema: {
2594
2594
  type: "object",
2595
- properties: { authToken: { type: "string", description: "Required when AUTOTAP_MCP_TOKEN is set" } },
2595
+ properties: { authToken: { type: "string", description: "Required when TAPP_MCP_TOKEN is set" } },
2596
2596
  },
2597
2597
  },
2598
2598
  ],
@@ -3450,7 +3450,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3450
3450
  if (!isNonEmptyString(args.goal)) return errorResult("goal is required");
3451
3451
  if (!isNonEmptyString(args.appBundleId)) return errorResult("appBundleId is required");
3452
3452
  const backend = resolveModelBackend();
3453
- if (!backend) return errorResult("AI-generate needs a model backend — set an Tapp subscription token (AUTOTAP_SUBSCRIPTION_TOKEN) or ANTHROPIC_API_KEY.");
3453
+ if (!backend) return errorResult("AI-generate needs a model backend — set TAPP_SUBSCRIPTION_TOKEN or ANTHROPIC_API_KEY.");
3454
3454
  const bundleId = args.appBundleId.trim();
3455
3455
 
3456
3456
  // 1) Grounding: reuse a capture's markers, else explore the app to build a screen/control map.
@@ -13,7 +13,7 @@ import { compileReleaseContract, loadReleaseContractFile } from "./release-contr
13
13
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
14
14
 
15
15
  function executionHome() {
16
- return process.env.AUTOTAP_HOME || process.env.TAPP_HOME || path.join(os.homedir(), ".tapp");
16
+ return process.env.TAPP_HOME || process.env.AUTOTAP_HOME || path.join(os.homedir(), ".tapp");
17
17
  }
18
18
 
19
19
  function atomicJson(destination, value) {
@@ -63,7 +63,7 @@ function artifactPaths(root, outDir = ".autotap") {
63
63
  }
64
64
 
65
65
  function productRunRoot(root) {
66
- const home = process.env.AUTOTAP_HOME || process.env.TAPP_HOME || path.join(os.homedir(), ".tapp");
66
+ const home = process.env.TAPP_HOME || process.env.AUTOTAP_HOME || path.join(os.homedir(), ".tapp");
67
67
  const identity = crypto.createHash("sha256").update(root).digest("hex").slice(0, 16);
68
68
  return path.join(home, "product-runs", identity);
69
69
  }
@@ -1,5 +1,5 @@
1
1
  // Pure report/gate logic shared by the MCP server (index.js) and the CI gate CLI
2
- // (ci-report.js). Turns a capture's OCQA markers into the same ship/no-ship report the AutoTap
2
+ // (ci-report.js). Turns a capture's OCQA markers into the same ship/no-ship report the Tapp
3
3
  // app produces, and diffs two runs' findings into the CI regression gate. No shell, no server —
4
4
  // keep it dependency-free so the CI path stays importable and testable.
5
5
  import fs from "fs";
@@ -78,7 +78,7 @@ export function parseOcqaMarkers(markersFilePath) {
78
78
  };
79
79
  }
80
80
 
81
- // Map harness OCQA_ISSUE `type` -> AutoTap FindingCategory. Crashes are always critical.
81
+ // Map harness OCQA_ISSUE `type` -> Tapp FindingCategory. Crashes are always critical.
82
82
  export const ISSUE_CATEGORY = {
83
83
  crash: "crash",
84
84
  app_hang: "app_hang",
@@ -100,7 +100,7 @@ export function severityRank(s) {
100
100
  return { critical: 0, high: 1, medium: 2, low: 3 }[s] ?? 4;
101
101
  }
102
102
 
103
- // Turn a capture's OCQA markers into the same ship/no-ship report the AutoTap app produces:
103
+ // Turn a capture's OCQA markers into the same ship/no-ship report Tapp produces:
104
104
  // deduped findings + a trustworthy verdict with a coverage floor (mirrors OrchestratorService).
105
105
  export function buildQaReport(markersFilePath, { platform = "ios" } = {}) {
106
106
  const base = parseOcqaMarkers(markersFilePath);