Skip to content

feat: Add CLI mode to VisionTest#32

Merged
docer1990 merged 16 commits into
mainfrom
feat/cli-mode
Apr 21, 2026
Merged

feat: Add CLI mode to VisionTest#32
docer1990 merged 16 commits into
mainfrom
feat/cli-mode

Conversation

@docer1990

Copy link
Copy Markdown
Owner

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 android or --platform ios (-p). When invoked with no arguments, VisionTest continues to start the MCP stdio server as before.

What changed

  • Refactored tool registrars — Extracted tool handler logic from inline MCP lambdas into named internal suspend functions, so both MCP and CLI share the same implementation per operation.
  • CLI framework (Clikt) — Added 13 subcommands (start_automation_server, screenshot, tap_by_coordinates, input_text, swipe_direction, etc.) with typed arguments and platform validation.
  • Exit code system — Structured exit codes (0-5) mapping domain exceptions to meaningful codes: 0 success, 1 generic failure, 2 usage error, 3 server not reachable, 4 device not found, 5 platform not supported.
  • Lazy ComponentHolder — Minimal DI graph that mirrors MCP wiring but initializes lazily, so --help doesn't trigger ADB connections.
  • Agent instructions auto-installinstall.sh now detects Claude Code, OpenCode, Codex, and Copilot CLI and installs VisionTest CLI usage instructions automatically (opt-out with --skip-agent-setup).
  • --local-jar flag for install.sh — Enables testing the full install flow without a GitHub Release.
  • TestsCliErrorHandlerTest (10 tests), VisionTestCliTest (12 tests), CliCommandIntegrationTest (7 integration tests with MockWebServer), plus unit tests for extracted registrar functions.
  • Documentation — Updated CLAUDE.md, README.md, CONTRIBUTING.md, docs/installation.md, added .claude/skills/visiontest-mobile/SKILL.md, and LEARNING.md entry.

How to test

./gradlew build          # compiles + runs all tests
./gradlew shadowJar      # builds fat JAR
java -jar app/build/libs/visiontest.jar --help
java -jar app/build/libs/visiontest.jar screenshot -p android

docer1990 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.
Copilot AI review requested due to automatic review settings April 21, 2026 14:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, --platform parsing, and a lazy CLI component graph.
  • Refactors Android/iOS tool registrars to extract MCP handler bodies into reusable internal suspend functions (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.

Comment thread app/src/main/kotlin/com/example/visiontest/tools/IOSAutomationToolRegistrar.kt Outdated
Comment thread app/src/test/kotlin/com/example/visiontest/cli/CliCommandIntegrationTest.kt Outdated
Comment thread app/src/main/kotlin/com/example/visiontest/cli/PlatformOption.kt
Comment thread app/src/main/kotlin/com/example/visiontest/Main.kt Outdated
Comment thread install.sh
Comment thread install.sh
- 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
@docer1990 docer1990 merged commit 5fdd3d9 into main Apr 21, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants