Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 36 additions & 49 deletions .github/workflows/smoke-copilot.lock.yml

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions .github/workflows/smoke-copilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ tools:
trusted-users:
- pelikhan
playwright:
mode: cli
web-fetch:
cli-proxy: true
runtimes:
Expand Down Expand Up @@ -142,7 +143,7 @@ Talk like a caveman in all your responses and outputs. Use short, broken sentenc

This workflow uses `cli-proxy: true`. The following MCP servers are **NOT available as MCP tools** — they are mounted exclusively as **shell CLI commands** (see `<mcp-clis>` section above). You **must** use them via the `bash` tool:

- **`playwright`** — use `playwright <tool> [--param value...]` in bash (e.g. `playwright browser_navigate --url ...`)
- **`playwright`** — installed as `@playwright/cli`, use `playwright-cli <command>` in bash (e.g. `playwright-cli open https://github.com`, `playwright-cli screenshot`)
- **`serena`** — use `serena <tool> [--param value...]` in bash (e.g. `serena activate_project --path ...`)
- **`agenticworkflows`** — use `agenticworkflows <tool> [--param value...]` in bash
- **`safeoutputs`** — use `safeoutputs <tool> [--param value...]` in bash (e.g. `safeoutputs add_comment --body "..."`)
Expand All @@ -161,7 +162,7 @@ These are **not** MCP protocol tools — they are bash executables. Call them wi
3. **Serena CLI Testing**:
- Use bash to run `serena activate_project --path ${{ github.workspace }}` to initialize the workspace and verify it succeeds (do NOT use bash to run go commands - use the serena CLI only)
- After initialization, use bash to run `serena find_symbol --name_path <symbol>` to search for symbols and verify that at least 3 symbols are found in the results
4. **Playwright CLI Testing**: Use bash to run `playwright browser_navigate --url https://github.com` to navigate to <https://github.com>, then `playwright browser_snapshot` to capture the page and verify the title contains "GitHub" (do NOT try to install playwright - use the `playwright` CLI command via bash only)
4. **Playwright CLI Testing**: Use bash to run `playwright-cli open https://github.com` to navigate to <https://github.com>, then `playwright-cli screenshot` to take a screenshot and verify that the output indicates a successful navigation to "GitHub" (do NOT try to install playwright - use the `playwright-cli` command via bash only)
5. **Web Fetch Testing**: Use the web-fetch tool to fetch https://github.com and verify the response contains "GitHub" (do NOT use bash or playwright for this test - use the web-fetch tool directly)
6. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-copilot-${{ github.run_id }}.txt` with content "Smoke test passed for Copilot at $(date)" (create the directory if it doesn't exist)
7. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
Expand Down
4 changes: 4 additions & 0 deletions pkg/constants/version_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ const MCPGIntegrityReactionsMinVersion Version = "v0.2.18"
// DefaultPlaywrightMCPVersion is the default version of the @playwright/mcp package
const DefaultPlaywrightMCPVersion Version = "0.0.73"

// DefaultPlaywrightCLIVersion is the default version of the @playwright/cli package

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.

Good addition of DefaultPlaywrightCLIVersion. Consider also adding a minimum version guard or a comment explaining why 0.1.11 was chosen as the baseline for CLI mode support.

// Used when tools.playwright.mode is "cli" to install the CLI tool instead of the MCP server.
const DefaultPlaywrightCLIVersion Version = "0.1.11"

// DefaultPlaywrightBrowserVersion is the default version of the Playwright browser Docker image
const DefaultPlaywrightBrowserVersion Version = "v1.59.1"

Expand Down
5 changes: 5 additions & 0 deletions pkg/parser/schemas/main_workflow_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4054,6 +4054,11 @@
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"description": "Integration mode: 'mcp' (default) runs a Docker-based MCP server; 'cli' installs @playwright/cli via npm for token-efficient CLI invocations.",
"enum": ["mcp", "cli"]
}
},
"additionalProperties": false
Expand Down
9 changes: 9 additions & 0 deletions pkg/workflow/compiler_yaml_main_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,15 @@ func (c *Compiler) generateEngineInstallAndPreAgentSteps(yaml *strings.Builder,
}
}

// Add Playwright CLI install steps when playwright is configured in CLI mode.
// These run after Node.js is available (set up by the engine install steps above).
for _, step := range generatePlaywrightCLIInstallSteps(data) {
for _, line := range step {
yaml.WriteString(line)
yaml.WriteByte('\n')
}
Comment on lines +336 to +342
}

// GH_AW_SAFE_OUTPUTS is now set at job level, no setup step needed

// Mint the GitHub MCP App token directly in the agent job.
Expand Down
11 changes: 7 additions & 4 deletions pkg/workflow/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ func collectDockerImages(tools map[string]any, workflowData *WorkflowData, actio
}

// Check for Playwright tool (uses Docker image - no version tag, only one image)
// Only in MCP mode; CLI mode installs @playwright/cli via npm instead.
if _, hasPlaywright := tools["playwright"]; hasPlaywright {
image := "mcr.microsoft.com/playwright/mcp"
if !imageSet[image] {
images = append(images, image)
imageSet[image] = true
if !isPlaywrightCLIMode(tools) {
image := "mcr.microsoft.com/playwright/mcp"
if !imageSet[image] {
images = append(images, image)
imageSet[image] = true
}
}
}

Expand Down
8 changes: 7 additions & 1 deletion pkg/workflow/mcp_cli_mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ func getMCPCLIServerNames(data *WorkflowData) []string {
// Only include tools that have MCP servers (skip bash, web-fetch, web-search, edit, cache-memory, etc.)
// Note: "github" is excluded — it is handled differently and should not be CLI-mounted.
switch toolName {
case "playwright", "qmd":
case "playwright":
// In CLI mode, playwright is installed as @playwright/cli via npm and is NOT
// an MCP server, so it must not appear in the CLI-mounted servers list.
if !isPlaywrightCLIMode(data.Tools) {
servers = append(servers, toolName)
}
case "qmd":
servers = append(servers, toolName)
case "agentic-workflows":
// The gateway and manifest use "agenticworkflows" (no hyphen) as the server ID.
Expand Down
5 changes: 5 additions & 0 deletions pkg/workflow/mcp_setup_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ func collectMCPTools(workflowData *WorkflowData) []string {
continue
}
if toolName == "github" || toolName == "playwright" || toolName == "cache-memory" || toolName == "agentic-workflows" {
// Playwright in CLI mode is not an MCP server; skip it here.
if toolName == "playwright" && isPlaywrightCLIMode(workflowData.Tools) {
mcpSetupGeneratorLog.Print("Skipping playwright MCP registration: tools.playwright.mode is cli")
continue
Comment on lines +143 to +146
}
mcpTools = append(mcpTools, toolName)
continue
}
Expand Down
92 changes: 92 additions & 0 deletions pkg/workflow/playwright_cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package workflow

// Package workflow provides support for Playwright CLI mode.
//
// # Playwright CLI Mode
//
// When tools.playwright.mode is set to "cli", the compiler installs the
// @playwright/cli npm package instead of launching the Docker-based MCP server.
// This is a token-efficient alternative for coding agents that prefer CLI-based
// workflows over MCP: CLI invocations avoid loading large tool schemas and verbose
// accessibility trees into the model context.
//
// See https://github.com/microsoft/playwright-cli for details.
//
// In CLI mode:
// - The mcr.microsoft.com/playwright/mcp Docker image is NOT pulled.
// - playwright is NOT registered as an MCP server in the gateway config.
// - @playwright/cli is installed via npm (global) before the agent runs.
// - playwright-cli install --skills installs agent skill files so the coding
// agent can discover and use the available playwright-cli commands.
// - The agent uses `playwright-cli <command>` directly via bash.
//
// Example workflow frontmatter:
//
// tools:
// playwright:
// mode: cli

import (
"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
)

var playwrightCLILog = logger.New("workflow:playwright_cli")

// isPlaywrightCLIMode returns true when the playwright tool in the given tools map
// is configured with mode: cli.
func isPlaywrightCLIMode(tools map[string]any) bool {
playwrightTool, ok := tools["playwright"]
if !ok || playwrightTool == false {
return false
}
config := parsePlaywrightTool(playwrightTool)
return config != nil && config.IsCLIMode()
}

// generatePlaywrightCLIInstallSteps returns npm install steps for @playwright/cli
// when playwright is configured in CLI mode. Returns nil if playwright is in MCP mode.
//
// Node.js setup is intentionally omitted here because all supported engines
// (copilot, claude, codex, gemini) include a Node.js setup step in their own
// installation steps, which run before this function is called.
func generatePlaywrightCLIInstallSteps(workflowData *WorkflowData) []GitHubActionStep {
if !isPlaywrightCLIMode(workflowData.Tools) {
return nil
}

playwrightCLILog.Print("Generating @playwright/cli install steps (CLI mode)")

version := string(constants.DefaultPlaywrightCLIVersion)
// Use version override from playwright config if provided
if playwrightTool, ok := workflowData.Tools["playwright"]; ok {
config := parsePlaywrightTool(playwrightTool)
if config != nil && config.Version != "" {
version = config.Version
playwrightCLILog.Printf("Using playwright CLI version from config: %s", version)
}
}

// Install @playwright/cli globally.
// runInstallScripts is true because playwright's postinstall script may set up
// browser binaries needed at runtime.
steps := GenerateNpmInstallStepsWithScope(
"@playwright/cli",
version,
"Install Playwright CLI",
"playwright-cli",
false, // Node.js is already set up by the engine install steps
true, // Global install so playwright-cli is on PATH
true, // Allow install scripts for browser setup
)

// Install playwright-cli skills so the coding agent can discover available commands.
installSkillsStep := GitHubActionStep{
" - name: Install Playwright CLI skills",
" run: playwright-cli install --skills",
}
steps = append(steps, installSkillsStep)

playwrightCLILog.Printf("Generated %d Playwright CLI install steps", len(steps))
return steps
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ jobs:
GH_AW_INFO_ENGINE_ID: "copilot"
GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
GH_AW_INFO_VERSION: "1.0.39"
GH_AW_INFO_AGENT_VERSION: "1.0.39"
GH_AW_INFO_VERSION: "1.0.40"
GH_AW_INFO_AGENT_VERSION: "1.0.40"
GH_AW_INFO_WORKFLOW_NAME: "basic-copilot-test"
GH_AW_INFO_EXPERIMENTAL: "false"
GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
Expand Down Expand Up @@ -310,7 +310,7 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.39
run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40
env:
GH_HOST: github.com
- name: Install AWF binary
Expand Down
Loading
Loading