@cityholdings/appium-node-cli
v0.4.0
Published
An interactive Appium shell powered by WebdriverIO
Maintainers
Readme
appium-node-cli
An interactive Appium client that feels like a database shell. It uses WebdriverIO in standalone mode and works with Android and iOS Appium drivers. It is a normal, deterministic CLI with no model provider or LLM SDK dependency.
Requirements
- Node.js 20+
- A reachable Appium server
- The appropriate Appium driver installed on the server:
uiautomator2for Android orxcuitestfor iOS - A connected/emulated device configured for that driver
Install
From this project:
npm install
npm run build
npm install -g .After publishing, any machine can install it with:
npm install -g @cityholdings/appium-node-cliPublishing (maintainers)
Publishing is automated by scripts/publish.sh, exposed as npm run release.
One-time setup:
Create an Automation (or Granular, publish-enabled) token at npmjs.com → Access Tokens.
Copy the token into a gitignored
.envfile (use.env.exampleas a template). The committed.npmrcreads it via${NPM_TOKEN}, so no secret is ever stored in git:read -rs -p "Paste NPM token: " TOK && printf 'NPM_TOKEN=%s\n' "$TOK" > .env && unset TOK chmod 600 .envMake sure the
@cityholdingsnpm org exists and your account can publish to it.
Release:
npm run release -- --dry-run # build + rehearse, no upload
npm run release # build + publish (public)The script loads NPM_TOKEN from .env, runs the build, and runs
npm publish. Bump the version first (npm version patch) if the current one
is already on the registry — npm will not overwrite an existing version.
Connect
Android:
appium-cli \
--server http://127.0.0.1:4723 \
--cap platformName=Android \
--cap appium:automationName=UiAutomator2 \
--cap appium:deviceName=AndroidiOS:
appium-cli \
--server http://127.0.0.1:4723 \
--cap platformName=iOS \
--cap appium:automationName=XCUITest \
--cap appium:deviceName=iPhone \
--cap appium:udid=autoFor real projects, keeping capabilities in JSON is easier:
{
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:deviceName": "Pixel",
"appium:appPackage": "com.example",
"appium:appActivity": ".MainActivity",
"appium:noReset": true
}appium-cli --caps-file ./capabilities.jsonThe URL may include a base path, such as
http://localhost:4723/wd/hub for older server configurations.
Interactive commands
appium> source
appium> screenshot current.png
appium> find "~Login"
appium> click "~Login"
appium> type "id=com.example:id/email" "[email protected]"
appium> swipe 500 1500 500 400 600
appium> activate-app com.example
appium> contexts
appium> helpSelectors are passed directly to WebdriverIO. Accessibility IDs (~Login),
Android resource IDs, XPath, iOS predicate strings, and class chains are
supported by the corresponding Appium driver.
Attach to an existing session
appium-cli \
--server http://127.0.0.1:4723 \
--session-id YOUR_SESSION_IDAttached sessions are left running by default. Add --delete-on-exit if the
CLI should terminate the attached session. The CLI reads the existing
session's capabilities from Appium before attaching, so native Android/iOS
sessions work without repeating capabilities and without WebdriverIO probing
the unsupported native /window endpoint.
Install the portable agent skill
The package includes a provider-neutral SKILL.md that teaches an agent how to
inspect, act, verify, reuse sessions, and safely close them.
npx @cityholdings/appium-node-cli install-skillBy default this installs to both Claude and Codex
(~/.claude/skills/appium-cli and ~/.codex/skills/appium-cli). Pass
--target with a comma-separated list to choose where it goes:
npx @cityholdings/appium-node-cli install-skill --target claude,codex
npx @cityholdings/appium-node-cli install-skill --target claude
npx @cityholdings/appium-node-cli install-skill --target codexAvailable targets:
| Target | Destination |
| --- | --- |
| claude | ~/.claude/skills/appium-cli (personal, all projects) |
| claude-project | ./.claude/skills/appium-cli (current project only) |
| codex | $CODEX_HOME/skills/appium-cli (default ~/.codex/skills) |
| agents | ~/.agents/skills/appium-cli |
| cursor | ./.cursor/skills/appium-cli |
| windsurf | ./.windsurf/skills/appium-cli |
| project | ./.agents/skills/appium-cli |
Use a custom destination for any other agent:
npx @cityholdings/appium-node-cli install-skill --path /path/to/agent/skills/appium-cliAdd --force to replace an existing copy; without it, a target that already
has the skill is skipped (other targets still install). The skill itself is
available at skills/appium-cli/SKILL.md in this package and does not depend on
Codex, OpenAI, or any particular model.
Automation-friendly mode
An external AI agent or script can call one command at a time and consume JSON:
appium-cli --caps-file capabilities.json --command "source" --jsonThe result has a stable envelope:
{"ok":true,"command":"source","data":"<hierarchy>...</hierarchy>"}Note that a one-shot invocation creates and closes a session. For multi-step automation, retain the new session:
appium-cli --caps-file capabilities.json \
--command "status" --json --keep-sessionRead data.sessionId, then attach for later commands:
appium-cli --session-id SESSION_ID --command "source" --json
appium-cli --session-id SESSION_ID --command 'click "~Login"' --jsonClose the retained session when finished:
appium-cli --session-id SESSION_ID \
--command "status" --json --delete-on-exitWith --json, interactive stdin/stdout is newline-delimited JSON without a
human prompt, so another process can keep the CLI alive and exchange commands
over pipes.
Security
Appium can control a real device. Keep the server on a trusted network, use authentication/TLS when exposed remotely, and avoid enabling Appium insecure features unless they are specifically needed.
