Skip to content

Commit 44c966b

Browse files
authored
feat: switch CLI long-press command to longpress (#80)
* fix: use longpress as sole CLI command and sync docs * fix: keep long-press as hidden longpress alias
1 parent 089cf96 commit 44c966b

12 files changed

Lines changed: 72 additions & 19 deletions

File tree

AGENTS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ Minimal operating guide for AI coding agents in this repo.
1818
- Transform tasks into verifiable goals with clear success criteria.
1919
- For multi-step tasks, state a brief plan with verification checkpoints.
2020

21+
## Docs & Skills
22+
- For every behavior or CLI surface change, evaluate whether docs/skills updates are required.
23+
- Update `README.md` and relevant `website/docs/**` pages when command behavior, flags, aliases, or workflows change.
24+
- Update relevant `skills/**/SKILL.md` guidance when usage examples or workflow recommendations change.
25+
- In final summaries, explicitly state whether docs/skills were updated; if not, state why no updates were needed.
26+
2127
## Scope
2228
- Solve issues with the smallest context read.
2329
- Keep changes scoped to one command family or module group.

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The project is in early development and considered experimental. Pull requests a
1414

1515
## Features
1616
- Platforms: iOS (simulator + physical device core automation) and Android (emulator + device).
17-
- Core commands: `open`, `back`, `home`, `app-switcher`, `press`, `long-press`, `focus`, `type`, `fill`, `scroll`, `scrollintoview`, `wait`, `alert`, `screenshot`, `close`, `reinstall`.
17+
- Core commands: `open`, `back`, `home`, `app-switcher`, `press`, `longpress`, `focus`, `type`, `fill`, `scroll`, `scrollintoview`, `wait`, `alert`, `screenshot`, `close`, `reinstall`.
1818
- Inspection commands: `snapshot` (accessibility tree), `appstate`, `apps`, `devices`.
1919
- Device tooling: `adb` (Android), `simctl`/`devicectl` (iOS via Xcode).
2020
- Minimal dependencies; TypeScript executed directly on Node 22+ (no build step).
@@ -118,7 +118,7 @@ agent-device trace stop ./trace.log
118118
```
119119

120120
Coordinates:
121-
- All coordinate-based commands (`press`, `long-press`, `swipe`, `focus`, `fill`) use device coordinates with origin at top-left.
121+
- All coordinate-based commands (`press`, `longpress`, `swipe`, `focus`, `fill`) use device coordinates with origin at top-left.
122122
- X increases to the right, Y increases downward.
123123
- `press` is the canonical tap command.
124124
- `click` is an equivalent alias and accepts the same targets (`x y`, `@ref`, selector) and flags.
@@ -136,7 +136,7 @@ agent-device swipe 540 1500 540 500 120 --count 8 --pause-ms 30 --pattern ping-p
136136
- `boot`, `open`, `close`, `reinstall`, `home`, `back`, `app-switcher`
137137
- `batch`
138138
- `snapshot`, `find`, `get`
139-
- `press` (alias: `click`), `focus`, `type`, `fill`, `long-press`, `swipe`, `scroll`, `scrollintoview`, `pinch`, `is`
139+
- `press` (alias: `click`), `focus`, `type`, `fill`, `longpress`, `swipe`, `scroll`, `scrollintoview`, `pinch`, `is`
140140
- `alert`, `wait`, `screenshot`
141141
- `trace start`, `trace stop`
142142
- `settings wifi|airplane|location on|off`
@@ -179,7 +179,7 @@ Pinch:
179179
Swipe timing:
180180
- `swipe` accepts optional `durationMs` (default `250`, range `16..10000`).
181181
- Android uses requested swipe duration directly.
182-
- iOS uses a safe normalized duration to avoid long-press side effects.
182+
- iOS uses a safe normalized duration to avoid longpress side effects.
183183

184184
## Skills
185185
Install the automation skills listed in [SKILL.md](skills/agent-device/SKILL.md).
@@ -304,7 +304,7 @@ Diagnostics files:
304304
- Built-in aliases include `Settings` for both platforms.
305305

306306
## iOS notes
307-
- Core runner commands: `snapshot`, `wait`, `click`, `fill`, `get`, `is`, `find`, `press`, `long-press`, `focus`, `type`, `scroll`, `scrollintoview`, `back`, `home`, `app-switcher`.
307+
- Core runner commands: `snapshot`, `wait`, `click`, `fill`, `get`, `is`, `find`, `press`, `longpress`, `focus`, `type`, `scroll`, `scrollintoview`, `back`, `home`, `app-switcher`.
308308
- Simulator-only commands: `alert`, `pinch`, `record`, `settings`.
309309
- iOS device runs require valid signing/provisioning (Automatic Signing recommended). Optional overrides: `AGENT_DEVICE_IOS_TEAM_ID`, `AGENT_DEVICE_IOS_SIGNING_IDENTITY`, `AGENT_DEVICE_IOS_PROVISIONING_PROFILE`.
310310

skills/agent-device/SKILL.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ agent-device press @e1 --count 5 # Repeat taps on the same target
126126
agent-device press @e1 --count 5 --double-tap # Use double-tap gesture per iteration
127127
agent-device swipe 540 1500 540 500 120
128128
agent-device swipe 540 1500 540 500 120 --count 8 --pause-ms 30 --pattern ping-pong
129-
agent-device long-press 300 500 800 # Long press (where supported)
129+
agent-device longpress 300 500 800 # Long press on iOS and Android
130130
agent-device scroll down 0.5
131131
agent-device pinch 2.0 # Zoom in 2x (iOS simulator only)
132132
agent-device pinch 0.5 200 400 # Zoom out at coordinates (iOS simulator only)
@@ -235,7 +235,8 @@ agent-device apps --platform android --user-installed
235235
- `press`/`click` support gesture series controls: `--count`, `--interval-ms`, `--hold-ms`, `--jitter-px`, `--double-tap`.
236236
- `--double-tap` cannot be combined with `--hold-ms` or `--jitter-px`.
237237
- `swipe` supports coordinate + timing controls and repeat patterns: `swipe x1 y1 x2 y2 [durationMs] --count --pause-ms --pattern`.
238-
- `swipe` timing is platform-safe: Android uses requested duration; iOS uses normalized safe timing to avoid long-press side effects.
238+
- `swipe` timing is platform-safe: Android uses requested duration; iOS uses normalized safe timing to avoid longpress side effects.
239+
- `longpress` is coordinate-based and supported on iOS and Android.
239240
- Pinch (`pinch <scale> [x y]`) is iOS simulator-only; scale > 1 zooms in, < 1 zooms out.
240241
- Snapshot refs are the core mechanism for interactive agent flows.
241242
- Use selectors for deterministic replay artifacts and assertions (e.g. in e2e test workflows).

src/__tests__/cli-help.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,21 @@ test('help appstate prints command help and skips daemon dispatch', async () =>
6969
assert.match(result.stdout, /Global flags:/);
7070
});
7171

72+
test('help longpress prints command help and skips daemon dispatch', async () => {
73+
const result = await runCliCapture(['help', 'longpress']);
74+
assert.equal(result.code, 0);
75+
assert.equal(result.daemonCalls, 0);
76+
assert.match(result.stdout, /Usage:\n agent-device longpress <x> <y> \[durationMs\]/);
77+
});
78+
79+
test('help long-press resolves to longpress help and skips daemon dispatch', async () => {
80+
const result = await runCliCapture(['help', 'long-press']);
81+
assert.equal(result.code, 0);
82+
assert.equal(result.daemonCalls, 0);
83+
assert.match(result.stdout, /Usage:\n agent-device longpress <x> <y> \[durationMs\]/);
84+
assert.doesNotMatch(result.stdout, /agent-device long-press/);
85+
});
86+
7287
test('appstate --help prints command help and skips daemon dispatch', async () => {
7388
const result = await runCliCapture(['appstate', '--help']);
7489
assert.equal(result.code, 0);

src/core/__tests__/capabilities.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ test('core commands support iOS simulator, iOS device, and Android', () => {
5959
'focus',
6060
'get',
6161
'home',
62-
'long-press',
62+
'longpress',
6363
'open',
6464
'press',
6565
'screenshot',

src/core/capabilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const COMMAND_CAPABILITY_MATRIX: Record<string, CommandCapability> = {
2828
get: { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },
2929
is: { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },
3030
home: { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },
31-
'long-press': { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },
31+
longpress: { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },
3232
open: { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },
3333
reinstall: { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },
3434
press: { ios: { simulator: true, device: true }, android: { emulator: true, device: true, unknown: true } },

src/core/dispatch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,12 @@ export async function dispatchCommand(
270270
pattern,
271271
};
272272
}
273-
case 'long-press': {
273+
case 'longpress': {
274274
const x = Number(positionals[0]);
275275
const y = Number(positionals[1]);
276276
const durationMs = positionals[2] ? Number(positionals[2]) : undefined;
277277
if (Number.isNaN(x) || Number.isNaN(y)) {
278-
throw new AppError('INVALID_ARGS', 'long-press requires x y [durationMs]');
278+
throw new AppError('INVALID_ARGS', 'longpress requires x y [durationMs]');
279279
}
280280
await interactor.longPress(x, y, durationMs);
281281
return { x, y, durationMs };

src/utils/__tests__/args.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,31 @@ test('parseArgs recognizes swipe positional + pattern flags', () => {
140140
assert.equal(parsed.flags.pattern, 'ping-pong');
141141
});
142142

143+
test('parseArgs recognizes longpress command', () => {
144+
const parsed = parseArgs(['longpress', '300', '500', '800'], { strictFlags: true });
145+
assert.equal(parsed.command, 'longpress');
146+
assert.deepEqual(parsed.positionals, ['300', '500', '800']);
147+
});
148+
149+
test('parseArgs supports legacy long-press alias', () => {
150+
const parsed = parseArgs(['long-press', '300', '500', '800'], { strictFlags: true });
151+
assert.equal(parsed.command, 'longpress');
152+
assert.deepEqual(parsed.positionals, ['300', '500', '800']);
153+
});
154+
155+
test('usageForCommand resolves longpress help', () => {
156+
const help = usageForCommand('longpress');
157+
assert.equal(help === null, false);
158+
assert.match(help ?? '', /agent-device longpress <x> <y> \[durationMs\]/);
159+
});
160+
161+
test('usageForCommand supports legacy long-press alias', () => {
162+
const help = usageForCommand('long-press');
163+
assert.equal(help === null, false);
164+
assert.match(help ?? '', /agent-device longpress <x> <y> \[durationMs\]/);
165+
assert.doesNotMatch(help ?? '', /agent-device long-press/);
166+
});
167+
143168
test('parseArgs rejects invalid swipe pattern', () => {
144169
assert.throws(
145170
() => parseArgs(['swipe', '0', '0', '10', '10', '--pattern', 'diagonal']),

src/utils/args.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ export function parseArgs(argv: string[], options?: ParseArgsOptions): ParsedArg
4343
continue;
4444
}
4545
if (!parseFlags) {
46-
if (!command) command = arg;
46+
if (!command) command = normalizeCommandAlias(arg);
4747
else positionals.push(arg);
4848
continue;
4949
}
5050
const isLongFlag = arg.startsWith('--');
5151
const isShortFlag = arg.startsWith('-') && arg.length > 1;
5252
if (!isLongFlag && !isShortFlag) {
53-
if (!command) command = arg;
53+
if (!command) command = normalizeCommandAlias(arg);
5454
else positionals.push(arg);
5555
continue;
5656
}
@@ -246,5 +246,10 @@ export function usage(): string {
246246
}
247247

248248
export function usageForCommand(command: string): string | null {
249-
return buildCommandUsageText(command);
249+
return buildCommandUsageText(normalizeCommandAlias(command));
250+
}
251+
252+
function normalizeCommandAlias(command: string): string {
253+
if (command === 'long-press') return 'longpress';
254+
return command;
250255
}

src/utils/command-schema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ export const COMMAND_SCHEMAS: Record<string, CommandSchema> = {
449449
allowsExtraPositionals: true,
450450
allowedFlags: ['count', 'intervalMs', 'holdMs', 'jitterPx', 'doubleTap', ...SELECTOR_SNAPSHOT_FLAGS],
451451
},
452-
'long-press': {
453-
description: 'Long press (where supported)',
452+
longpress: {
453+
description: 'Long press by coordinates (iOS and Android)',
454454
positionalArgs: ['x', 'y', 'durationMs?'],
455455
allowedFlags: [],
456456
},

0 commit comments

Comments
 (0)