mobile-ai-agents 1.0.37 → 1.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/AGENTS.md +10 -0
  2. package/CLAUDE.md +4 -0
  3. package/README.md +51 -5
  4. package/agents/cross-platform/appforge/agent.md +4 -2
  5. package/agents/cross-platform/figma/agent.md +65 -47
  6. package/agents/cross-platform/mobile-harness/agent.md +1 -1
  7. package/cli/commands/figma.js +42 -0
  8. package/cli/commands/network.js +19 -0
  9. package/cli/commands/router.js +4 -0
  10. package/cli/fixtures/network-demo.js +52 -0
  11. package/cli/index.js +22 -0
  12. package/cli/lib/network-capture.js +192 -0
  13. package/docs/figma-mcp.md +131 -0
  14. package/docs/website-api-discovery.md +111 -0
  15. package/docs/wiki.md +9 -0
  16. package/install.sh +2 -0
  17. package/package.json +7 -2
  18. package/skills/cross-platform/figma-mcp-setup.md +60 -0
  19. package/skills/cross-platform/figma-spec-harvest.md +103 -0
  20. package/skills/cross-platform/figma-to-implementation.md +90 -0
  21. package/skills/cross-platform/prd-verification.md +7 -0
  22. package/skills/cross-platform/web-network-to-mobile.md +139 -0
  23. package/tools/figma-spec/README.md +86 -0
  24. package/tools/figma-spec/compare_screens.py +111 -0
  25. package/tools/figma-spec/crosscheck.py +112 -0
  26. package/tools/figma-spec/dump_to_yaml.py +132 -0
  27. package/tools/figma-spec/node_dump.js +237 -0
  28. package/tools/figma-spec/page_index.js +44 -0
  29. package/tools/figma-spec/requirements.txt +3 -0
  30. package/tools/figma-spec/text_dump.js +68 -0
  31. package/tools/figma-spec/validate_spec.py +153 -0
  32. package/tools/figma-spec/yaml_io.py +22 -0
  33. package/workflows/figma-to-code.md +26 -0
  34. package/workflows/new-screen.md +2 -2
@@ -0,0 +1,192 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { createRequire } = require('node:module');
6
+
7
+ function origin(value) {
8
+ const url = new URL(value);
9
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) {
10
+ throw new Error('Use an HTTP(S) URL without embedded credentials.');
11
+ }
12
+ return url.origin;
13
+ }
14
+
15
+ // Values are deliberately omitted, including harmless-looking IDs and query strings.
16
+ function safeUrl(value) {
17
+ const url = new URL(value);
18
+ const pathname = url.pathname.split('/').map(part => /^[a-z][a-z_-]{0,40}$/i.test(part) ? part : part ? '[REDACTED]' : '').join('/');
19
+ return url.origin + pathname + (url.search ? '?' + [...url.searchParams.keys()].map(key => `${encodeURIComponent(key)}=[REDACTED]`).join('&') : '');
20
+ }
21
+
22
+ function shape(value, depth = 0) {
23
+ if (depth > 12) return { type: 'unknown', reason: 'depth-limit' };
24
+ if (value === null) return { type: 'null' };
25
+ if (Array.isArray(value)) {
26
+ const variants = [...new Set(value.slice(0, 20).map(item => JSON.stringify(shape(item, depth + 1))))].map(item => JSON.parse(item));
27
+ return { type: 'array', observedItems: variants, sampled: value.length > 20 };
28
+ }
29
+ if (typeof value === 'object') return { type: 'object', properties: Object.fromEntries(Object.entries(value).map(([key, item]) => [key, shape(item, depth + 1)])) };
30
+ return { type: typeof value };
31
+ }
32
+
33
+ function bodyShape(text, contentType = '') {
34
+ if (!text) return { state: 'empty' };
35
+ if (Buffer.byteLength(text) > 65536) return { state: 'omitted', reason: 'body-size-limit' };
36
+ if (/json/i.test(contentType)) {
37
+ try { return { state: 'observed', schema: shape(JSON.parse(text)) }; }
38
+ catch { return { state: 'unavailable', reason: 'invalid-json' }; }
39
+ }
40
+ if (/application\/x-www-form-urlencoded/i.test(contentType)) {
41
+ return { state: 'observed', schema: { type: 'object', properties: Object.fromEntries([...new URLSearchParams(text).keys()].map(key => [key, { type: 'string' }])) } };
42
+ }
43
+ return { state: 'omitted', reason: 'non-json-body' };
44
+ }
45
+
46
+ function safeHeaders(headers) {
47
+ return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key, ['content-type', 'accept'].includes(key.toLowerCase()) ? value.split(';')[0] : '[REDACTED]']));
48
+ }
49
+
50
+ function validateFlow(flow) {
51
+ if (!Array.isArray(flow) || flow.length > 50) throw new Error('Flow must be an array with at most 50 steps.');
52
+ for (const step of flow) {
53
+ if (!step || !['click', 'fill'].includes(step.action) || typeof step.selector !== 'string' || !step.selector || step.selector.length > 500) throw new Error('Each step needs action click|fill and a selector.');
54
+ if (step.action === 'fill' && typeof step.value !== 'string') throw new Error('Fill steps need a string value; use test data only.');
55
+ if (step.waitMs !== undefined && (!Number.isInteger(step.waitMs) || step.waitMs < 0 || step.waitMs > 10000)) throw new Error('waitMs must be an integer from 0 to 10000.');
56
+ }
57
+ }
58
+
59
+ function loadPlaywright() {
60
+ try { return require('playwright'); }
61
+ catch {
62
+ try { return createRequire(path.join(process.cwd(), 'package.json'))('playwright'); }
63
+ catch { throw new Error('Optional browser tooling missing. Install playwright in your project, then run: npx playwright install chromium'); }
64
+ }
65
+ }
66
+
67
+ async function capture({ url, flow = [], allowedOrigins = [], allowWrites = false, output, headless = true, channel, playwright }) {
68
+ const firstOrigin = origin(url);
69
+ const allowed = new Set([firstOrigin, ...allowedOrigins.map(origin)]);
70
+ validateFlow(flow);
71
+ if (!output) throw new Error('Choose an output directory.');
72
+ // Every run gets a fresh directory: never overwrite an existing report or follow it as a symlink.
73
+ fs.mkdirSync(output, { recursive: false });
74
+ const report = {
75
+ schemaVersion: 1, startedAt: new Date().toISOString(), source: safeUrl(url),
76
+ viewport: { width: 390, height: 844 }, status: 'capturing',
77
+ privacy: 'Body values omitted; header values and query values redacted. Review field names and route segments before sharing.',
78
+ limitations: [
79
+ 'Touchpoint attribution is temporal, not proof of causality; delayed work may belong to a later window.',
80
+ 'Fresh isolated browser; no existing login session imported. Service workers blocked and routing disables HTTP cache.',
81
+ 'No screenshots, DOM text, raw HAR, SSE messages or multipart values saved. WebSocket connections are blocked.',
82
+ 'Observed shapes are samples, not complete API contracts. No mobile implementation or mobile parity verified.',
83
+ 'One page supported; popups blocked. JSON bodies capped at 64 KiB after transfer; request count capped at 500.',
84
+ 'Same-origin analytics are not automatically classified as product APIs. UI state is metadata only, not business outcome proof.',
85
+ ], touchpoints: [], requests: [],
86
+ };
87
+ let browser, context, current, stopped = false;
88
+ const records = new Map();
89
+ const pending = new Set();
90
+ const track = promise => { pending.add(promise); promise.finally(() => pending.delete(promise)); };
91
+ const flush = async () => { await Promise.allSettled([...pending]); };
92
+ const save = () => {
93
+ fs.writeFileSync(path.join(output, 'capture.json'), JSON.stringify(report, null, 2));
94
+ const lines = ['# Website API capture', '', `Status: ${report.status}`, '', 'Body values are omitted. No mobile parity has been verified.', '', '## Touchpoints', ''];
95
+ for (const point of report.touchpoints) {
96
+ lines.push(`### ${point.id}: ${point.action}`, '', `Result: ${point.status}. Requests: ${point.requestIds.join(', ') || 'none observed in this window'}.`, '');
97
+ for (const id of point.requestIds) {
98
+ const item = report.requests.find(r => r.id === id);
99
+ lines.push(`- ${id}: ${item.method} ${item.url.replace(/[\r\n`<>]/g, '')} — ${item.status || item.outcome}`);
100
+ }
101
+ lines.push('');
102
+ }
103
+ lines.push('## Limitations', '', ...report.limitations.map(item => `- ${item}`));
104
+ fs.writeFileSync(path.join(output, 'report.md'), lines.join('\n') + '\n');
105
+ };
106
+ try {
107
+ const pw = playwright || loadPlaywright();
108
+ report.tool = { name: 'playwright', node: process.version };
109
+ browser = await pw.chromium.launch({ headless, ...(channel ? { channel } : {}) });
110
+ report.tool.browser = browser.version();
111
+ context = await browser.newContext({ viewport: report.viewport, serviceWorkers: 'block', acceptDownloads: false });
112
+ if (typeof context.routeWebSocket !== 'function') throw new Error('Install a current Playwright version with WebSocket routing support.');
113
+ await context.routeWebSocket('**/*', socket => socket.close());
114
+ context.setDefaultTimeout(10000);
115
+ const page = await context.newPage();
116
+ context.on('page', popup => { if (popup !== page) popup.close().catch(() => {}); });
117
+ context.on('request', request => {
118
+ if (stopped || report.requests.length >= 500) return;
119
+ const headers = request.headers();
120
+ const record = {
121
+ id: `R${report.requests.length + 1}`, touchpoint: current?.id || null,
122
+ at: new Date().toISOString(), method: request.method(), url: safeUrl(request.url()),
123
+ resourceType: request.resourceType(), requestHeaders: safeHeaders(headers),
124
+ auth: headers.authorization ? 'authorization-header' : headers.cookie ? 'cookie' : 'not-observed',
125
+ requestBody: bodyShape(request.postData(), headers['content-type']), outcome: 'pending',
126
+ redirectedFrom: records.get(request.redirectedFrom())?.id || null,
127
+ };
128
+ records.set(request, record); report.requests.push(record); current?.requestIds.push(record.id);
129
+ });
130
+ await context.route('**/*', async route => {
131
+ const request = route.request();
132
+ const record = records.get(request);
133
+ let reason;
134
+ if (report.requests.length >= 500) reason = 'request-limit';
135
+ else if (!allowed.has(new URL(request.url()).origin)) reason = 'outside-allowed-origins';
136
+ else if (!allowWrites && !['GET', 'HEAD', 'OPTIONS'].includes(request.method())) reason = 'writes-disabled';
137
+ if (reason) { if (record) record.blockedReason = reason; await route.abort(); }
138
+ else await route.continue();
139
+ });
140
+ context.on('requestfailed', request => {
141
+ const record = records.get(request);
142
+ if (record) { record.outcome = 'failed'; record.failure = record.blockedReason || 'network-failure'; }
143
+ });
144
+ context.on('response', response => {
145
+ const record = records.get(response.request());
146
+ if (!record) return;
147
+ record.status = response.status(); record.responseHeaders = safeHeaders(response.headers());
148
+ record.outcome = response.ok() ? 'http-success' : 'http-error';
149
+ track((async () => {
150
+ const type = response.headers()['content-type'] || '';
151
+ if (!/json/i.test(type)) { record.responseBody = { state: 'omitted', reason: 'non-json-body' }; return; }
152
+ if (Number(response.headers()['content-length']) > 65536) { record.responseBody = { state: 'omitted', reason: 'body-size-limit' }; return; }
153
+ // A stalled body cannot hold finalization indefinitely.
154
+ let timer;
155
+ try {
156
+ const text = await Promise.race([response.text(), new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('timeout')), 5000); })]);
157
+ record.responseBody = bodyShape(text, type);
158
+ } catch { record.responseBody = { state: 'unavailable', reason: 'body-read-failed-or-timeout' }; }
159
+ finally { clearTimeout(timer); }
160
+ })());
161
+ });
162
+ const state = async () => ({
163
+ url: safeUrl(page.url() === 'about:blank' ? url : page.url()),
164
+ invalidInputs: await page.locator('input:invalid,select:invalid,textarea:invalid').count().catch(() => null),
165
+ });
166
+ const window = async (action, execute, waitMs = 500) => {
167
+ current = { id: `T${report.touchpoints.length + 1}`, action, startedAt: new Date().toISOString(), requestIds: [], before: await state(), status: 'running' };
168
+ report.touchpoints.push(current);
169
+ try { await execute(); await page.waitForTimeout(waitMs); current.status = 'completed'; }
170
+ catch { current.status = 'failed'; throw new Error('Browser action failed; check selector, access and tooling. Sensitive browser error details omitted.'); }
171
+ finally { await flush(); current.after = await state(); current.finishedAt = new Date().toISOString(); }
172
+ };
173
+ await window('navigate', () => page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15000 }));
174
+ for (const step of flow) {
175
+ await window(step.action, () => step.action === 'fill' ? page.locator(step.selector).fill(step.value) : page.locator(step.selector).click(), step.waitMs ?? 500);
176
+ }
177
+ report.status = report.requests.length >= 500 || report.requests.some(r => r.outcome === 'pending' || r.outcome === 'failed' || r.outcome === 'http-error' || r.responseBody?.state === 'unavailable') ? 'partial' : 'captured';
178
+ } catch (error) {
179
+ report.status = 'failed';
180
+ // Only our own actionable setup errors are returned; browser messages may contain secrets.
181
+ report.error = /Optional browser tooling|Browser action failed/.test(error.message) ? error.message : 'Browser capture unavailable; check Playwright/browser installation and scoped site access.';
182
+ } finally {
183
+ stopped = true;
184
+ await context?.close().catch(() => {});
185
+ await flush();
186
+ await browser?.close().catch(() => {});
187
+ report.finishedAt = new Date().toISOString(); save();
188
+ }
189
+ return report;
190
+ }
191
+
192
+ module.exports = { capture, origin, safeUrl, shape, bodyShape, safeHeaders, validateFlow };
@@ -0,0 +1,131 @@
1
+ # Figma MCP: setup, specs, and implementation
2
+
3
+ Connect your coding assistant once, extract a design contract, then implement it in your project's own framework. Installing this toolkit installs prompts; it does **not** connect your Figma account or grant access to files.
4
+
5
+ ## 1. Connect Figma
6
+
7
+ Use the official remote endpoint `https://mcp.figma.com/mcp` and complete Figma OAuth. No personal access token is needed for this route. Choose one setup below; preserve your existing MCP servers when adding configuration.
8
+
9
+ ### Codex
10
+
11
+ In the Codex app, open Plugins, find Figma, install it, and complete authentication. Alternatively, with Codex CLI installed:
12
+
13
+ ```sh
14
+ codex mcp add figma --url https://mcp.figma.com/mcp
15
+ codex mcp login figma
16
+ codex mcp list
17
+ ```
18
+
19
+ ### Claude Code
20
+
21
+ ```sh
22
+ claude mcp add --transport http figma https://mcp.figma.com/mcp
23
+ ```
24
+
25
+ Start a new session, run `/mcp`, select Figma, and authenticate. Use `--scope user` on the add command if you want it across projects.
26
+
27
+ ### Cursor
28
+
29
+ Use `/add-plugin figma` in agent chat and authenticate, or follow Figma's manual Cursor installation guide linked below.
30
+
31
+ ### VS Code
32
+
33
+ Run **MCP: Open Workspace Folder MCP Configuration** (or **MCP: Open User Configuration** for global setup). Merge this entry into the `servers` object:
34
+
35
+ ```json
36
+ {
37
+ "servers": {
38
+ "figma": {
39
+ "type": "http",
40
+ "url": "https://mcp.figma.com/mcp"
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Start the server and complete authentication. This `servers` shape belongs to VS Code; do not paste it into clients expecting `mcpServers` or TOML.
47
+
48
+ ### Desktop alternative
49
+
50
+ Open the file in Figma desktop, switch to Dev Mode, and enable the desktop MCP server in the inspect panel. Configure your supported client with Streamable HTTP URL `http://127.0.0.1:3845/mcp`. Keep Figma desktop running on the same machine as the MCP client. Desktop access depends on your seat/plan; consult current Figma documentation. Selection-based prompts are available here; use explicit frame links for reproducible handoffs.
51
+
52
+ ### Check the connection
53
+
54
+ Copy a frame's link, including `node-id`, from a file your authenticated account can access. Ask:
55
+
56
+ ```text
57
+ Use Figma MCP to read this frame's metadata and capture a reference screenshot.
58
+ Report the file key, node ID, tools available, and any access failures.
59
+ Do not change the Figma file or app code.
60
+ URL: paste your frame link here
61
+ ```
62
+
63
+ Success means a real node read and screenshot, not just a configured server. Tool names and capabilities vary by client and server. Load the Figma plugin's prerequisite skills when supplied, especially before `get_design_context` or `use_figma`. `use_figma` is optional; do not require it for basic design-to-code.
64
+
65
+ ## 2. Load the toolkit workflows
66
+
67
+ ```sh
68
+ npx mobile-ai-agents install
69
+ ```
70
+
71
+ Select your coding tool and platform. Install the portable extraction and verification helpers inside your target project:
72
+
73
+ ```sh
74
+ npx mobile-ai-agents figma tools init
75
+ ```
76
+
77
+ This preserves existing helper files. Read `tools/figma-spec/README.md` for Python prerequisites, raw-dump conversion, strict validation and screenshot comparison. In a checkout of this toolkit, you can also run `node /absolute/path/to/mobile-dev-skills/cli/index.js figma tools init` from the target project. Available starting with v1.0.38.
78
+
79
+ The three Figma skills are included for every platform. In tools supporting the installed slash commands:
80
+
81
+ ```text
82
+ /figma-mcp-setup
83
+ Help me connect Figma MCP and verify a read of my frame.
84
+
85
+ /figma-spec-harvest
86
+ URL: paste your frame link here
87
+ FEATURE: checkout
88
+ SCOPE: this frame and its variants
89
+ Extract written requirements, exact available values, assets, and reference screenshots.
90
+
91
+ /figma-to-implementation
92
+ SPEC: docs/design-spec/checkout
93
+ PLATFORM: detect from this repository
94
+ Implement the verified scope and report requirement and screenshot evidence.
95
+ ```
96
+
97
+ For tools without those commands, ask the assistant to read the corresponding `skills/cross-platform/figma-*.md` files. Use `/figma-to-code` for the combined workflow; `/figma` remains the component translation agent. There is no automatic Figma authentication during toolkit installation. The existing `mobile-ai-agents mcp setup` command configures **Mobile MCP device QA**, not Figma.
98
+
99
+ ## 3. Platform mapping
100
+
101
+ | Target | Native implementation | Verification |
102
+ |---|---|---|
103
+ | Android | Compose or existing XML Views; semantic theme tokens | Gradle checks and emulator/device captures |
104
+ | iOS | SwiftUI or existing UIKit; dynamic type and safe areas | Xcode build/tests and simulator/device captures |
105
+ | Flutter | Existing Dart state management, bounded flex/scroll layout | Analyze, widget tests, golden/device captures |
106
+ | React Native | Existing JS/TS components and navigation | Project checks and iOS/Android captures |
107
+ | Kotlin Multiplatform | Existing shared UI or separate native UIs | Checks/captures per rendered target |
108
+ | Unity | Existing UI Toolkit or uGUI; canvas scaling and anchors | Editor/player checks and target resolution captures |
109
+ | Unreal | Existing UMG/Slate; anchors and DPI scaling | Compile/automation and viewport captures |
110
+ | Web or other framework | User-selected repository stack | Native build/tests and browser/runtime captures |
111
+
112
+ Figma pixels describe the reference canvas. Record how they map to logical dp/pt/framework units and responsive constraints; do not use device pixel ratio as a universal conversion. A generated React/Tailwind snippet is evidence about the design, not a reason to change the project's platform.
113
+
114
+ ## Troubleshooting
115
+
116
+ - **No tools:** restart the client/session, confirm the server is enabled, and check client support. If both remote and desktop are configured, confirm which server is selected.
117
+ - **Access denied:** authenticate the account that can open the design; a shared URL does not guarantee API access.
118
+ - **Rate limit:** record incomplete scope, retry within server guidance, and resume from saved evidence. Do not report a partial harvest as complete.
119
+ - **Missing exact values or written notes:** request the missing export/data or use read-only file inspection if available. Record unknowns; screenshots cannot prove exact token values or hidden behavior.
120
+ - **Missing assets/fonts:** list the gap and resolve licensing/availability before substituting. Respect the client's media-download rules.
121
+ - **Only web code returned:** specify the target framework, reuse repository components, and select the matching Code Connect label when available.
122
+
123
+ ## Sources and validation
124
+
125
+ Setup checked against official documentation on 2026-10-08:
126
+ - [Figma remote setup](https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/)
127
+ - [Figma desktop setup](https://developers.figma.com/docs/figma-mcp-server/local-server-installation/)
128
+ - [Figma tools and prompts](https://developers.figma.com/docs/figma-mcp-server/tools-and-prompts/)
129
+ - [OpenAI Codex MCP configuration](https://developers.openai.com/codex/mcp/)
130
+
131
+ Adapted from the seller-app's `figma-spec-harvest`, `figma-spec-harvester`, `figma-to-flutter`, `figma-to-implementation`, and `design-sync` workflows. No seller-app file IDs, business copy, or package paths are included. Live Figma extraction and per-platform compilation must be validated in users' connected projects; they were not run when authoring this guide.
@@ -0,0 +1,111 @@
1
+ # Website API discovery for mobile developers
2
+
3
+ Capture a website's HTTP requests and response shapes at each interaction, then hand the evidence to your AI coding agent. This is useful when a web/backend team has shipped a feature and Android, iOS, Flutter, React Native or Kotlin Multiplatform developers need to implement the same API flow.
4
+
5
+ The runnable command delivers **browser network evidence**, not generated mobile clients or verified mobile parity. Use the [web-network-to-mobile skill](../skills/cross-platform/web-network-to-mobile.md) for that handoff. Follow-up work is tracked in [#70–#72](https://github.com/salmanashraf/mobile-ai-agents/issues/70).
6
+
7
+ ## Run from a checkout
8
+
9
+ Browser tooling is optional and is not installed with the toolkit. In a project with a package.json:
10
+
11
+ ```bash
12
+ npm install --no-save playwright
13
+ npx playwright install chromium
14
+ ```
15
+
16
+ From this repository, start the owned demo (terminal 1):
17
+
18
+ ```bash
19
+ node cli/fixtures/network-demo.js /tmp/mobile-api-flow.json
20
+ ```
21
+
22
+ Capture the demo (terminal 2):
23
+
24
+ ```bash
25
+ node cli/index.js network capture \
26
+ --url http://127.0.0.1:4173 \
27
+ --flow /tmp/mobile-api-flow.json \
28
+ --out /tmp/mobile-api-demo \
29
+ --allow-writes
30
+ ```
31
+
32
+ Use a new flow-file path and output directory on subsequent runs; existing outputs are never overwritten. Stop the demo with Ctrl+C. If using installed Google Chrome rather than Playwright Chromium, add `--channel chrome`. `--headed` shows the isolated browser. The module never connects to your personal browser profile.
33
+
34
+ Available starting in `mobile-ai-agents@1.0.39`. For your own authorized site, use `npx mobile-ai-agents@latest network capture --url URL --out NEW_DIRECTORY`. The owned fixture above runs from a checkout. Playwright must be installed in your project; the optional loader resolves it from the current working directory.
35
+
36
+ ## Expected demo evidence
37
+
38
+ The fixture exercises six touchpoints:
39
+
40
+ | Touchpoint | Action | Expected product calls |
41
+ |---|---|---|
42
+ | T1 | Navigate | HTML document; no initial product API call |
43
+ | T2 | Load listings | `GET /api/items` |
44
+ | T3 | Open detail | `GET /api/detail` and `GET /api/options` |
45
+ | T4 | Validate empty title locally | None |
46
+ | T5 | Fill title | None |
47
+ | T6 | Save test listing | `POST /api/save` → 201 |
48
+
49
+ `capture.json` contains timestamped request IDs, touchpoint windows, methods, sanitized URLs and headers, status codes, observed body schemas, failures and redirect links. `report.md` provides a readable index. Body scalar values are omitted rather than guessed to be safe. For example, an email response field is represented as `{"type":"string"}`, not its actual value.
50
+
51
+ ## Capture your own authorized test flow
52
+
53
+ Create a JSON array of bounded actions using stable CSS selectors:
54
+
55
+ ```json
56
+ [
57
+ { "action": "click", "selector": "[data-testid=load-listings]", "waitMs": 1000 },
58
+ { "action": "fill", "selector": "input[name=title]", "value": "Test listing" },
59
+ { "action": "click", "selector": "[data-testid=save]", "waitMs": 1500 }
60
+ ]
61
+ ```
62
+
63
+ ```bash
64
+ node cli/index.js network capture \
65
+ --url https://staging.example.com/listings \
66
+ --allow-origin https://api.staging.example.com \
67
+ --flow flow.json \
68
+ --out ./api-capture-run-1
69
+ ```
70
+
71
+ Initial navigation is always captured before the steps. Without `--flow`, only initial navigation is inspected. Additional allowed origins are comma-separated; all unlisted HTTP(S) origins are blocked, including third-party analytics. WebSocket connections are blocked and require a current Playwright version with `routeWebSocket` support. Same-origin analytics remain unclassified in the report. Route interception also disables HTTP cache and service workers are blocked so this is a controlled capture rather than faithful cache behavior. There is no crawler, manual interactive recorder, existing-session import, or automatic login bypass.
72
+
73
+ Only GET, HEAD and OPTIONS are allowed by default. `--allow-writes` enables other HTTP methods for the supplied flow; use it only for authorized test data and resettable environments. Read-like GraphQL POST requests also require it. An HTTP method alone cannot establish that an endpoint is safe; even GET can have side effects. The tool does not retry or replay failed writes.
74
+
75
+ ## Evidence limits and privacy
76
+
77
+ - Attribution is by the active time window, not a causal dependency graph. A delayed request may appear under a later touchpoint. Choose waits appropriate to the test and inspect the timestamps.
78
+ - JSON and URL-encoded request shapes and JSON response shapes are sampled observations. Requiredness, enum ranges and all error responses cannot be inferred from a single run. GraphQL operation values, socket/SSE messages, multipart values and binary data are not recorded.
79
+ - Query values, URL credentials, fragments and non-content header values are removed/redacted. Numeric/opaque route segments are redacted. Readable path segments, origins, query keys and JSON property names remain; review them before sharing. This is not a guarantee of anonymous artifacts.
80
+ - No screenshot, DOM text, raw HAR, auth storage or body scalar values are written. Input flow files can contain sensitive test values; keep them local. Redirect links use captured request IDs. Auth presence is a hint from exposed header names, not a complete authentication contract.
81
+ - Fresh context, one page, 390×844 viewport; popups are closed. Default action timeout 10 seconds, navigation timeout 15 seconds, body-read timeout 5 seconds, at most 50 actions, 500 captured requests, 64 KiB body inspection, and depth/array sampling limits. Body transfer itself is not capped when content length is unavailable. Do not use large download/streaming flows.
82
+ - `captured` means scoped browser windows completed without recorded failures. HTTP errors, blocked requests, pending requests or unavailable JSON bodies yield `partial`; action/browser failures yield `failed`. Partial/failed runs return a nonzero exit code and retain evidence. Neither status proves mobile parity.
83
+
84
+ ## Mobile integration handoff
85
+
86
+ Give your coding agent the report, the current app repository, and an explicit target:
87
+
88
+ ```text
89
+ Use skills/cross-platform/web-network-to-mobile.md.
90
+ Read /tmp/mobile-api-demo/capture.json and report.md as observed browser evidence.
91
+ Map each touchpoint to the existing Flutter app's networking and UI architecture.
92
+ Cross-check the backend contract before resolving optional fields or auth.
93
+ Implement the scoped flow, then verify actual mobile requests and UI behavior.
94
+ Keep missing runtime evidence unverified.
95
+ ```
96
+
97
+ Use the same process for Android, iOS, React Native and KMP; tooling and runtime proof are target-specific. Mobile MCP UI screenshots do not establish network parity. Use app-owned debug instrumentation or authorized server correlation for the network side.
98
+
99
+ ## Maintainer decisions and validation
100
+
101
+ Issue [#69](https://github.com/salmanashraf/mobile-ai-agents/issues/69) uses an optional Playwright adapter, a fresh browser context and an owned demo instead of a default browser dependency. CLI command modules and fixtures are already inside the package's shipped `cli/` directory. This resolves the capture-specific adapter/demo choices from #63; the broader MIRROR design/build inventory remains separate. Reuse #54/#55 for device QA/demo and #57 for visual checks rather than duplicating them here.
102
+
103
+ Run deterministic checks with `npm test`. Browser tests skip when Playwright is absent; require real browser validation with:
104
+
105
+ ```bash
106
+ NETWORK_BROWSER_TESTS=1 NETWORK_BROWSER_CHANNEL=chrome node --test test/network-capture.test.js
107
+ ```
108
+
109
+ Remove `NETWORK_BROWSER_CHANNEL=chrome` to use installed Playwright Chromium. Browser tests use only a loopback fixture and cover observed shapes, zero/multiple calls, blocked writes/origins, HTTP/network failures and failed actions. They do not validate any mobile runtime.
110
+
111
+ Discoverability phrases used here describe the delivered behavior: **website API discovery**, **browser network capture**, **HTTP request and response analysis**, and **mobile API integration**. Search-volume and ranking data have not been measured; keywords do not guarantee Google/GitHub placement or virality.
package/docs/wiki.md CHANGED
@@ -768,3 +768,12 @@ A: Yes. The repo is MIT licensed — free to use, modify, and incorporate into c
768
768
  ---
769
769
 
770
770
  *Last updated: v1.0.25 — August 2026*
771
+
772
+ ## Figma MCP and Design Handoff
773
+
774
+ Connect your coding assistant using the [Figma MCP guide](figma-mcp.md). Use `/figma-spec-harvest` for source-traced specs and `/figma-to-implementation` to build in the existing platform. `/figma-to-code` chains setup, harvest, implementation and evidence checks. Install portable helpers with `npx mobile-ai-agents figma tools init`; this preserves existing files and does not authenticate Figma. APPFORGE, Mobile Harness and `/prd-verification` carry node/AC traceability into planning and verification.
775
+
776
+
777
+ ## Website API discovery and mobile handoff
778
+
779
+ Starting in v1.0.39, `npx mobile-ai-agents@latest network capture --url URL --out NEW_DIRECTORY` records bounded website flows with optional Playwright. The `/web-network-to-mobile` skill is installed across platform bundles and maps browser request/response evidence to the existing mobile stack. See [the setup and owned demo](website-api-discovery.md). Captures are observed shapes with temporal attribution; mobile implementation and actual network/UI verification remain separate.
package/install.sh CHANGED
@@ -93,6 +93,7 @@ _agent_lines() {
93
93
  # Outputs: "<slug> <src_path>" lines
94
94
  _skill_lines() {
95
95
  # Cross-platform skills (always)
96
+ echo "web-network-to-mobile skills/cross-platform/web-network-to-mobile.md"
96
97
  echo "grill-mobile skills/cross-platform/grill-mobile.md"
97
98
  echo "crash-triage skills/cross-platform/crash-triage.md"
98
99
  echo "perf-audit skills/cross-platform/perf-audit.md"
@@ -215,6 +216,7 @@ install_claude() {
215
216
  echo " /security-scanner /store-listing-writer"
216
217
  echo ""
217
218
  echo " Cross-platform skills:"
219
+ echo " /web-network-to-mobile"
218
220
  echo " /grill-mobile /clean-code-audit /security-audit /crash-triage"
219
221
  echo " /perf-audit /store-listing /release-prep"
220
222
  echo " /feature-slice /mobile-mcp-qa /mobile-memory-save /mobile-memory-graph"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobile-ai-agents",
3
- "version": "1.0.37",
3
+ "version": "1.0.39",
4
4
  "description": "AI agents, skills, and Loop Engineering workflows for building, testing, auditing, and releasing mobile apps.",
5
5
  "bin": {
6
6
  "mobile-ai-agents": "cli/index.js",
@@ -19,7 +19,8 @@
19
19
  "mobile-karpathy.md",
20
20
  "skills/",
21
21
  "templates/",
22
- "workflows/"
22
+ "workflows/",
23
+ "tools/figma-spec/"
23
24
  ],
24
25
  "keywords": [
25
26
  "mobile",
@@ -40,6 +41,10 @@
40
41
  "skills",
41
42
  "loop-engineering",
42
43
  "mobile-harness",
44
+ "api-discovery",
45
+ "network-traffic-analysis",
46
+ "browser-automation",
47
+ "api-integration",
43
48
  "llm",
44
49
  "cursor",
45
50
  "windsurf"
@@ -0,0 +1,60 @@
1
+ # Skill — /figma-mcp-setup
2
+
3
+ **Platform:** Cross-Platform
4
+ **Category:** UI & Design
5
+ **Composable With:** FIGMA, /figma-spec-harvest, /figma-to-implementation, /prd-verification, /mobile-mcp-qa
6
+
7
+ ## Purpose
8
+
9
+ Connect a supported assistant to official Figma MCP and verify read access.
10
+
11
+ ## When to Use
12
+
13
+ - First Figma connection or authentication troubleshooting.
14
+ - Before harvesting a design when MCP tools are missing.
15
+
16
+ ## Inputs
17
+
18
+ Client name/version, remote or desktop preference, and a frame URL for verification.
19
+
20
+ ## Skill Prompt
21
+
22
+ ```text
23
+ You help the user configure official Figma MCP for their chosen coding client.
24
+ Prefer the official remote server https://mcp.figma.com/mcp with OAuth. Offer
25
+ http://127.0.0.1:3845/mcp when desktop selection access is wanted and supported.
26
+ Identify the client before choosing its configuration syntax. Follow the current
27
+ official Figma setup guide; use docs/figma-mcp.md when present. Preserve existing
28
+ servers. Installing toolkit prompts does not configure MCP or authenticate Figma.
29
+ Do not print credentials. Let the user complete OAuth in the client's flow.
30
+ Discover available tools and load any required vendor skills before calling them.
31
+ Verify with a real read of the supplied node plus reference screenshot. Never edit
32
+ Figma or app code during setup. If no URL is provided, configuration can be ready
33
+ but access remains unverified. Do not claim a live check from static configuration.
34
+ Return: CLIENT, SERVER, CONFIGURATION (ready|missing), AUTH (verified|unverified),
35
+ READ (pass|fail|unverified), SCREENSHOT (pass|fail|unverified), NEXT ACTION.
36
+ ```
37
+
38
+ ## Example
39
+
40
+ **Input:** `Client: VS Code. Remote. No frame URL yet. Existing configuration contains figma.url=https://mcp.figma.com/mcp and type=http.`
41
+
42
+ **Output:**
43
+ ```text
44
+ CLIENT: VS Code
45
+ SERVER: https://mcp.figma.com/mcp
46
+ CONFIGURATION: ready
47
+ AUTH: unverified
48
+ READ: unverified
49
+ SCREENSHOT: unverified
50
+ NEXT ACTION: Start the server, authenticate, and supply an accessible frame link.
51
+ ```
52
+
53
+ ## Composition Example
54
+
55
+ Use this skill before `/figma-spec-harvest`; proceed when node access is verified.
56
+
57
+ ## Notes
58
+
59
+ - Official setup and client-specific examples: https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/
60
+ - Tested with: authored/reviewed in Codex; no live OAuth or Figma read test performed.