feat: Add CLI mode to VisionTest#32
Merged
Merged
Conversation
added 15 commits
April 21, 2026 12:00
… for CLI reuse Move each MCP tool's business logic from inline lambda into a named internal suspend function on its registrar. MCP registrations become thin wrappers (arg extraction + delegation). This enables the upcoming CLI subcommands to call the same functions directly, sharing one implementation per operation. No MCP behavior change. Add targeted unit tests for the extracted functions on all four registrars (AndroidDevice, IOSDevice, AndroidAutomation).
- CliExit exception + ExitCode enum (6 exit codes: 0-5) - CliErrorHandler.runCliCommand() maps exceptions to exit codes - PlatformOption: reusable --platform/-p option + androidOnly guard - ComponentHolder: minimal DI graph mirroring MCP wiring - VisionTestCli: 13 stub subcommands wired in - All tasks.md Phase 3 items marked complete
- 13 command files under cli/commands/ with typed Clikt args - VisionTestCli wires real commands via ComponentHolder - Android-only commands (install, press_back) use requireAndroid guard - Cross-platform commands dispatch via when(platform) - launchApp delegates to device registrars (not automation)
- CliErrorHandler catches NoDeviceAvailableException/NoSimulatorAvailableException -> exit 4 - Added requireServerRunning() helper for CLI commands - ComponentHolder.isServerRunning() dispatches to platform client - 9 automation commands now check server status before delegating - Clikt UsageError -> exit 2, uncaught Exception -> exit 1 (already in place)
- CliErrorHandlerTest: 10 tests covering all exit code mappings - VisionTestCliTest: 12 tests for Clikt arg parsing, platform validation, choice validation - CliCommandIntegrationTest: 7 integration tests with MockWebServer fakes - Refactored CliErrorHandler: extracted executeCliCommand() for testability - Made ComponentHolder constructor internal for test injection - All tests green (app + automation-server)
- Created .claude/skills/visiontest-mobile/SKILL.md with automation loop, exit codes, examples - Added CLI Usage section to CLAUDE.md with command table and exit codes - Added 'Dual facade: MCP + CLI' entry to LEARNING.md - Updated docs/installation.md with CLI usage note
- Switched from Clikt main() to parse() for proper exit code control - UsageError/MissingArgument now correctly maps to exit 2 (was 1) - PrintHelpMessage handled separately to show formatted help text - All smoke tests pass: --help (0), missing arg (2), bad choice (2), android-only rejects ios (5), platform not supported (5) - ./gradlew build, test, and shadowJar all pass - All tasks.md items marked complete
- Fix requireAndroid() placement: moved inside runCliCommand blocks so all exceptions flow through the CLI error handler consistently - Lazy ComponentHolder: VisionTestCli now uses lazy initialization so --help doesn't trigger ADB connections or shutdown hooks - Platform enum: replaced string-based platform with exhaustive enum, removing all dead else branches across 13 commands - androidOnlyPlatformOption: now only accepts 'android' via Clikt choice, removing need for runtime requireAndroid() check entirely - Deduplicated isServerRunning guards: extracted requireServer() helper in both Android and iOS registrars (22 replacements total) - Documented intentional double health check (CLI + registrar) with rationale - Updated README.md with CLI usage section and exit codes - Updated CONTRIBUTING.md with CLI project structure, test coverage, and extending instructions
install.sh now detects Claude Code, OpenCode, Codex, and Copilot CLI and installs VisionTest CLI usage instructions using idempotent marker-based updates. Adds AGENT_INSTRUCTIONS.md as canonical source and includes it in GitHub Release assets. Supports --skip-agent-setup.
Allows testing the full install flow without a GitHub Release by pointing to a locally built JAR. Documented in CONTRIBUTING.md.
This reverts commit 5ee19da.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a first-class Clikt-based CLI facade to the VisionTest Kotlin/JVM app, allowing the same automation operations available via MCP tools to be invoked from the command line while preserving the existing no-arg MCP stdio server behavior.
Changes:
- Adds a CLI root command plus 13 subcommands, shared exit-code/error handling,
--platformparsing, and a lazy CLI component graph. - Refactors Android/iOS tool registrars to extract MCP handler bodies into reusable
internal suspendfunctions (shared by MCP and CLI). - Updates installer/docs/release workflow to ship and auto-install agent CLI instructions (
AGENT_INSTRUCTIONS.md) and adds CLI-related tests.
Reviewed changes
Copilot reviewed 44 out of 44 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| openspec/changes/add-cli-mode/tasks.md | Spec-driven task checklist for CLI mode implementation. |
| openspec/changes/add-cli-mode/specs/cli-mode/spec.md | Formal requirements/spec for CLI mode, exit codes, and platform behavior. |
| openspec/changes/add-cli-mode/proposal.md | Motivation and high-level plan for CLI facade. |
| openspec/changes/add-cli-mode/design.md | Design decisions for dispatch, extraction, and UX contracts. |
| openspec/changes/add-cli-mode/.openspec.yaml | OpenSpec metadata for this change set. |
| install.sh | Adds --local-jar, agent-instructions download, and auto-config for detected agents. |
| docs/installation.md | Documents agent-instructions installation and CLI usage basics. |
| .github/workflows/release.yaml | Publishes AGENT_INSTRUCTIONS.md as a release asset. |
| AGENT_INSTRUCTIONS.md | New distributable CLI instructions content for agents/skills. |
| app/build.gradle.kts | Adds Clikt dependency for CLI parsing. |
| app/src/main/kotlin/com/example/visiontest/Main.kt | Adds arg-based dispatch between MCP server mode and CLI mode. |
| app/src/main/kotlin/com/example/visiontest/cli/VisionTestCli.kt | CLI root command registering the 13 MVP subcommands with lazy components. |
| app/src/main/kotlin/com/example/visiontest/cli/PlatformOption.kt | Defines Platform enum and reusable --platform/-p option helpers. |
| app/src/main/kotlin/com/example/visiontest/cli/ComponentHolder.kt | Lazy CLI object graph mirroring ToolFactory wiring + shutdown hook. |
| app/src/main/kotlin/com/example/visiontest/cli/CliExit.kt | Defines CliExit + fixed exit code enum. |
| app/src/main/kotlin/com/example/visiontest/cli/CliErrorHandler.kt | Implements runCliCommand/executeCliCommand and server-reachability guard. |
| app/src/main/kotlin/com/example/visiontest/cli/commands/InstallAutomationServerCommand.kt | CLI command: install automation server APKs (Android-only). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/StartAutomationServerCommand.kt | CLI command: start automation server (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/AutomationServerStatusCommand.kt | CLI command: server status (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/GetInteractiveElementsCommand.kt | CLI command: interactive elements (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/GetUiHierarchyCommand.kt | CLI command: UI hierarchy (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/GetDeviceInfoCommand.kt | CLI command: device info (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/ScreenshotCommand.kt | CLI command: screenshot (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/TapByCoordinatesCommand.kt | CLI command: tap by coordinates (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/InputTextCommand.kt | CLI command: input text (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/SwipeDirectionCommand.kt | CLI command: swipe direction (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/PressBackCommand.kt | CLI command: press back (Android-only). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/PressHomeCommand.kt | CLI command: press home (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/cli/commands/LaunchAppCommand.kt | CLI command: launch app (Android/iOS). |
| app/src/main/kotlin/com/example/visiontest/tools/AndroidDeviceToolRegistrar.kt | Extracts device-tool bodies into internal suspend functions. |
| app/src/main/kotlin/com/example/visiontest/tools/IOSDeviceToolRegistrar.kt | Extracts iOS device-tool bodies into internal suspend functions. |
| app/src/main/kotlin/com/example/visiontest/tools/AndroidAutomationToolRegistrar.kt | Extracts automation-tool bodies into internal suspend functions + shared server-guard. |
| app/src/main/kotlin/com/example/visiontest/tools/IOSAutomationToolRegistrar.kt | Extracts iOS automation-tool bodies into internal suspend functions + shared server-guard. |
| app/src/test/kotlin/com/example/visiontest/MainDispatchTest.kt | Tests route selection between MCP and CLI based on args. |
| app/src/test/kotlin/com/example/visiontest/cli/VisionTestCliTest.kt | Tests platform option parsing and argument validation for CLI shapes. |
| app/src/test/kotlin/com/example/visiontest/cli/CliErrorHandlerTest.kt | Tests exit-code mapping in CLI error handler. |
| app/src/test/kotlin/com/example/visiontest/cli/CliCommandIntegrationTest.kt | Integration-style CLI delegation tests with fake backends/MockWebServer. |
| app/src/test/kotlin/com/example/visiontest/tools/AndroidDeviceToolRegistrarTest.kt | Tests extracted Android device registrar functions directly. |
| app/src/test/kotlin/com/example/visiontest/tools/IOSDeviceToolRegistrarTest.kt | Tests extracted iOS device registrar functions directly. |
| app/src/test/kotlin/com/example/visiontest/tools/AndroidAutomationToolRegistrarTest.kt | Tests extracted Android automation registrar functions directly (MockWebServer). |
| README.md | Adds CLI usage + exit codes, updates future plans checklist. |
| CLAUDE.md | Adds CLI usage reference and exit-code table for agents. |
| CONTRIBUTING.md | Documents CLI code layout and testing/installer workflow. |
| LEARNING.md | Documents “dual facade” design (MCP + CLI) and handler extraction rationale. |
- requireServer() now throws ServerNotRunningException instead of returning error string, closing the TOCTOU race between CLI pre-check and registrar - androidOnlyPlatformOption() accepts both platforms at parse time; commands call requireAndroid() in run() to produce exit code 5 (not 2) - runCli catches CliktError and exits non-zero via stderr (was exit 0/stdout) - Remove cmd.parse() call in integration test that triggered exitProcess - install.sh --local-jar without path argument now exits 2 immediately - install.sh download_agent_instructions handles missing curl/wget gracefully
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add CLI mode to VisionTest
Summary
Adds a full CLI interface to VisionTest so the same automation operations available via MCP tools can be invoked directly from the command line. Every command requires
--platform androidor--platform ios(-p). When invoked with no arguments, VisionTest continues to start the MCP stdio server as before.What changed
internal suspendfunctions, so both MCP and CLI share the same implementation per operation.start_automation_server,screenshot,tap_by_coordinates,input_text,swipe_direction, etc.) with typed arguments and platform validation.0success,1generic failure,2usage error,3server not reachable,4device not found,5platform not supported.--helpdoesn't trigger ADB connections.install.shnow detects Claude Code, OpenCode, Codex, and Copilot CLI and installs VisionTest CLI usage instructions automatically (opt-out with--skip-agent-setup).--local-jarflag for install.sh — Enables testing the full install flow without a GitHub Release.CliErrorHandlerTest(10 tests),VisionTestCliTest(12 tests),CliCommandIntegrationTest(7 integration tests with MockWebServer), plus unit tests for extracted registrar functions.docs/installation.md, added.claude/skills/visiontest-mobile/SKILL.md, and LEARNING.md entry.How to test