Skip to content

feat: add visiontest init command for project-level agent setup#33

Open
docer1990 wants to merge 3 commits into
mainfrom
feat/add-init-command
Open

feat: add visiontest init command for project-level agent setup#33
docer1990 wants to merge 3 commits into
mainfrom
feat/add-init-command

Conversation

@docer1990

Copy link
Copy Markdown
Owner

Summary

Adds visiontest init --agent claude,opencode,codex — a CLI command that writes project-level SKILL.md files so AI coding agents discover VisionTest instructions automatically. Replaces the global home-directory agent setup previously done by install.sh.

Motivation

install.sh mixed two concerns: binary installation and agent configuration. Global home-directory config files can't be shared via version control, so teammates had to run the installer independently. Project-level SKILL.md files are committable and shareable.

What changed

New

  • InitCommand.kt — Clikt command with --agent option (comma-separated, validated). Writes SKILL.md with YAML frontmatter to each agent's project-level path.
  • Gradle processResources task — copies AGENT_INSTRUCTIONS.md into the JAR as a classpath resource. No network needed at init time.
  • 8 unit tests — single/multi agent, invalid name, blank name, missing flag, idempotency, path mapping, classpath resource loading.

Modified

  • install.sh — removed ~120 lines of agent setup code (download_agent_instructions, install_agent_instructions, append_with_markers, --skip-agent-setup flag). Summary now points users to visiontest init.
  • release.yaml — removed AGENT_INSTRUCTIONS.md from release assets.
  • CLAUDE.md, README.md, docs/installation.md — document the init command.

Agent path mapping

Agent Project path
claude .claude/skills/visiontest/SKILL.md
opencode .opencode/skills/visiontest/SKILL.md
codex .agents/skills/visiontest/SKILL.md

Usage

# In any project directory
visiontest init --agent claude,opencode,codex
Testing
- ./gradlew :app:test — all passing
- bash -n install.sh — syntax check passing

Copilot AI review requested due to automatic review settings April 21, 2026 15:11

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

Adds a first-class CLI surface (alongside the existing MCP stdio server) and introduces visiontest init --agent ... to generate project-local SKILL.md files for supported coding agents using instructions embedded in the JAR.

Changes:

  • Add init subcommand that writes agent-specific project paths with YAML frontmatter + embedded instructions content.
  • Add CLI mode (Clikt) with argument-based dispatch in Main.kt, plus shared registrar “extracted” suspend functions to reuse MCP business logic.
  • Update installer, docs, and release workflow assumptions; add unit/integration-style tests for CLI and extracted registrars.

Reviewed changes

Copilot reviewed 50 out of 50 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
openspec/changes/add-init-command/tasks.md Task breakdown for init command + resource embedding work
openspec/changes/add-init-command/specs/init-command/spec.md Spec for visiontest init behavior, paths, output, and errors
openspec/changes/add-init-command/proposal.md Rationale and scope for project-local agent setup via init
openspec/changes/add-init-command/design.md Design decisions for embedded resource + agent mapping + no platform flag
openspec/changes/add-init-command/.openspec.yaml Openspec metadata for init command change
openspec/changes/add-cli-mode/tasks.md Task plan for CLI mode, dispatch, refactor, and tests
openspec/changes/add-cli-mode/specs/cli-mode/spec.md Spec for MCP-vs-CLI dispatch, platform flag, exit codes, and MVP commands
openspec/changes/add-cli-mode/proposal.md Rationale for adding CLI facade and shared implementation
openspec/changes/add-cli-mode/design.md Detailed design decisions for CLI mode and handler extraction
openspec/changes/add-cli-mode/.openspec.yaml Openspec metadata for CLI mode change
install.sh Removes agent setup, adds --local-jar install path, updates summary output
docs/installation.md Documents CLI usage and visiontest init guidance post-install
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/AndroidDeviceToolRegistrarTest.kt Tests extracted Android device registrar functions directly
app/src/test/kotlin/com/example/visiontest/tools/AndroidAutomationToolRegistrarTest.kt Tests extracted Android automation registrar functions with MockWebServer
app/src/test/kotlin/com/example/visiontest/cli/VisionTestCliTest.kt CLI option parsing tests (platform, args, direction choice)
app/src/test/kotlin/com/example/visiontest/cli/InitCommandTest.kt Tests init command parsing, mapping, idempotency, and classpath resource load
app/src/test/kotlin/com/example/visiontest/cli/CliErrorHandlerTest.kt Tests exit-code mapping behavior of CLI error handler
app/src/test/kotlin/com/example/visiontest/cli/CliCommandIntegrationTest.kt Integration-style delegation tests for CLI path using mocked backends
app/src/test/kotlin/com/example/visiontest/MainDispatchTest.kt Tests MCP-vs-CLI routing based on args
app/src/main/kotlin/com/example/visiontest/tools/IOSDeviceToolRegistrar.kt Extracts internal suspend functions for iOS device tools; MCP delegates
app/src/main/kotlin/com/example/visiontest/tools/IOSAutomationToolRegistrar.kt Extracts internal suspend functions for iOS automation tools; MCP delegates
app/src/main/kotlin/com/example/visiontest/tools/AndroidDeviceToolRegistrar.kt Extracts internal suspend functions for Android device tools; MCP delegates
app/src/main/kotlin/com/example/visiontest/tools/AndroidAutomationToolRegistrar.kt Extracts internal suspend functions for Android automation tools; MCP delegates
app/src/main/kotlin/com/example/visiontest/cli/commands/TapByCoordinatesCommand.kt Adds CLI tap command
app/src/main/kotlin/com/example/visiontest/cli/commands/SwipeDirectionCommand.kt Adds CLI swipe-by-direction command
app/src/main/kotlin/com/example/visiontest/cli/commands/StartAutomationServerCommand.kt Adds CLI start server command
app/src/main/kotlin/com/example/visiontest/cli/commands/ScreenshotCommand.kt Adds CLI screenshot command
app/src/main/kotlin/com/example/visiontest/cli/commands/PressHomeCommand.kt Adds CLI press-home command
app/src/main/kotlin/com/example/visiontest/cli/commands/PressBackCommand.kt Adds CLI press-back (Android-only) command
app/src/main/kotlin/com/example/visiontest/cli/commands/LaunchAppCommand.kt Adds CLI launch-app command
app/src/main/kotlin/com/example/visiontest/cli/commands/InstallAutomationServerCommand.kt Adds CLI install automation server (Android-only) command
app/src/main/kotlin/com/example/visiontest/cli/commands/InputTextCommand.kt Adds CLI input-text command
app/src/main/kotlin/com/example/visiontest/cli/commands/InitCommand.kt Adds visiontest init command to write SKILL.md files from embedded instructions
app/src/main/kotlin/com/example/visiontest/cli/commands/GetUiHierarchyCommand.kt Adds CLI UI-hierarchy command
app/src/main/kotlin/com/example/visiontest/cli/commands/GetInteractiveElementsCommand.kt Adds CLI interactive-elements command
app/src/main/kotlin/com/example/visiontest/cli/commands/GetDeviceInfoCommand.kt Adds CLI device-info command
app/src/main/kotlin/com/example/visiontest/cli/commands/AutomationServerStatusCommand.kt Adds CLI server-status command
app/src/main/kotlin/com/example/visiontest/cli/VisionTestCli.kt Root CLI command wiring for subcommands (lazy ComponentHolder)
app/src/main/kotlin/com/example/visiontest/cli/PlatformOption.kt Adds Platform enum and reusable --platform option helpers
app/src/main/kotlin/com/example/visiontest/cli/ComponentHolder.kt CLI object graph wiring mirroring MCP path + shutdown hook
app/src/main/kotlin/com/example/visiontest/cli/CliExit.kt Adds typed CLI exit code enum + CliExit exception
app/src/main/kotlin/com/example/visiontest/cli/CliErrorHandler.kt Adds exit-code gateway and server-running guard helper
app/src/main/kotlin/com/example/visiontest/Main.kt Dispatches between MCP server and CLI based on args; handles CLI errors/help
app/build.gradle.kts Adds Clikt dependency and embeds AGENT_INSTRUCTIONS.md into resources
README.md Documents CLI usage, init agent setup, and exit codes
LEARNING.md Documents dual-facade pattern and handler extraction rationale
CONTRIBUTING.md Updates repo structure and testing notes; adds installer testing section
CLAUDE.md Adds CLI usage and exit code documentation for agents
AGENT_INSTRUCTIONS.md New shared instruction content embedded into JAR for init

Comment thread app/src/test/kotlin/com/example/visiontest/cli/CliCommandIntegrationTest.kt Outdated
Comment thread app/src/test/kotlin/com/example/visiontest/cli/VisionTestCliTest.kt
Comment thread app/src/test/kotlin/com/example/visiontest/cli/VisionTestCliTest.kt
Comment thread install.sh
Comment thread CONTRIBUTING.md
Comment thread app/src/main/kotlin/com/example/visiontest/Main.kt Outdated
Comment thread app/src/main/kotlin/com/example/visiontest/cli/PlatformOption.kt
docer1990 added 3 commits April 21, 2026 17:47
Implement 'visiontest init --agent claude,opencode,codex' that writes
SKILL.md files to each agent's project-level directory. Instructions are
embedded as a JAR resource via Gradle processResources — no network needed.

- InitCommand with comma-separated --agent validation and YAML frontmatter
- 7 unit tests covering single/multi agent, errors, idempotency
- Remove all agent setup code from install.sh (~120 lines)
- Remove AGENT_INSTRUCTIONS.md from release workflow assets
- Update CLAUDE.md, README.md, docs/installation.md
- Close InputStream with .use {} to prevent resource leak
- Validate blank agent names in comma-separated list
- Use CliktError instead of error() for missing JAR resource
- Use JUnit @tempdir for automatic test cleanup
- Add stricter YAML frontmatter assertions in tests
- Add test for blank agent name edge case
- Mark all tasks.md checkboxes as complete

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

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment on lines +54 to +77
override fun run() {
// Validate agent names
val invalid = agents.filter { it.isBlank() || it !in AGENT_PATHS }
if (invalid.isNotEmpty()) {
throw UsageError(
"Unknown agent(s): ${invalid.joinToString()}. Valid agents: ${AGENT_PATHS.keys.joinToString()}"
)
}

val instructions = resourceLoader(RESOURCE_PATH)
?: throw CliktError("Internal error: embedded resource '$RESOURCE_PATH' not found in JAR")

val content = YAML_FRONTMATTER + instructions

for (agent in agents) {
val relativePath = AGENT_PATHS.getValue(agent)
val target = workingDir.resolve(relativePath)
target.parent.createDirectories()
target.writeText(content)
echo(" wrote $target")
}

echo("Initialized ${agents.size} agent skill file(s).")
}

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

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

InitCommand is the only CLI subcommand that doesn’t route its work through runCliCommand like the rest of the commands (e.g., AutomationServerStatusCommand), so it bypasses the project’s standard stdout/stderr + exit-code mapping layer. To keep CLI behavior consistent and make future error handling/testing uniform, consider wrapping the command body with runCliCommand and returning the combined output string (instead of calling echo directly).

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +53
private val agents by option("--agent", help = "Comma-separated agent names: claude, opencode, codex")
.split(",")
.required()

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

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

--agent values are split on commas but not trimmed, so inputs like --agent claude, opencode will produce an unexpected “unknown agent” error because of the leading space. Consider trimming each entry after splitting (and validating on the trimmed values) to make the flag more robust.

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +61
// Validate agent names
val invalid = agents.filter { it.isBlank() || it !in AGENT_PATHS }
if (invalid.isNotEmpty()) {
throw UsageError(
"Unknown agent(s): ${invalid.joinToString()}. Valid agents: ${AGENT_PATHS.keys.joinToString()}"
)
}

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

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

When the --agent list contains a blank entry (e.g., --agent ,claude or claude,), the error message currently renders as Unknown agent(s): . ... because the invalid value is an empty string. Handling blank entries separately (with an explicit message like “blank agent name in --agent list”) would make the failure actionable.

Copilot uses AI. Check for mistakes.
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