From 812fdf01aaf204875a96b69b84e27513ca974e04 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 02:35:09 +0000
Subject: [PATCH 1/7] Initial plan
From 78a1d9ac3a7d802c07c8dac3b47cd3729ef2627d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 03:06:52 +0000
Subject: [PATCH 2/7] feat: Add tools.playwright.mode: cli|mcp for playwright
approach selection
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/md/playwright_cli_prompt.md | 14 +
pkg/parser/schemas/main_workflow_schema.json | 5 +
pkg/workflow/claude_tools.go | 13 +-
pkg/workflow/codex_engine.go | 2 +-
.../compiler_orchestrator_workflow.go | 5 +
pkg/workflow/compiler_string_api.go | 5 +
pkg/workflow/compiler_yaml_main_job.go | 11 +
pkg/workflow/docker.go | 15 +-
pkg/workflow/mcp_detection.go | 9 +-
pkg/workflow/mcp_setup_generator.go | 8 +-
pkg/workflow/playwright_cli_test.go | 442 ++++++++++++++++++
pkg/workflow/prompt_constants.go | 1 +
pkg/workflow/prompts.go | 8 +
pkg/workflow/runtime_step_generator.go | 22 +
pkg/workflow/tools_parser.go | 5 +
pkg/workflow/tools_types.go | 7 +
pkg/workflow/tools_validation.go | 17 +
pkg/workflow/unified_prompt_step.go | 18 +-
18 files changed, 588 insertions(+), 19 deletions(-)
create mode 100644 actions/setup/md/playwright_cli_prompt.md
create mode 100644 pkg/workflow/playwright_cli_test.go
diff --git a/actions/setup/md/playwright_cli_prompt.md b/actions/setup/md/playwright_cli_prompt.md
new file mode 100644
index 00000000000..dd521a0e0b8
--- /dev/null
+++ b/actions/setup/md/playwright_cli_prompt.md
@@ -0,0 +1,14 @@
+
+Playwright CLI is available for browser automation tasks. Use bash to run playwright-cli commands directly on the runner.
+
+playwright-cli is installed and available as a command-line tool. Use the bash tool to run it. Common usage patterns:
+
+- Screenshot a URL: `playwright-cli screenshot https://example.com screenshot.png`
+- Open a browser interactively (for scripting): `playwright-cli open https://example.com`
+- Generate a test script: `playwright-cli codegen https://example.com`
+
+Output files should be saved to /tmp/gh-aw/ for access by subsequent steps.
+
+Refer to `playwright-cli --help` for the full list of commands and options available.
+
+
diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json
index d0e54ea6bbf..b62016567a3 100644
--- a/pkg/parser/schemas/main_workflow_schema.json
+++ b/pkg/parser/schemas/main_workflow_schema.json
@@ -3428,6 +3428,11 @@
"type": "object",
"description": "Playwright tool configuration with custom version and arguments",
"properties": {
+ "mode": {
+ "type": "string",
+ "description": "Playwright integration mode: 'mcp' (default) uses the Playwright MCP Docker container, 'cli' installs playwright-cli directly on the runner",
+ "enum": ["cli", "mcp"]
+ },
"version": {
"type": ["string", "number"],
"description": "Optional Playwright container version (e.g., 'v1.41.0', 1.41, 20). Numeric values are automatically converted to strings at runtime.",
diff --git a/pkg/workflow/claude_tools.go b/pkg/workflow/claude_tools.go
index 176d2670ebe..c2c8c7bc15d 100644
--- a/pkg/workflow/claude_tools.go
+++ b/pkg/workflow/claude_tools.go
@@ -99,12 +99,15 @@ func (e *ClaudeEngine) expandNeutralToolsToClaudeTools(tools map[string]any) map
}
// Handle playwright tool by converting it to an MCP tool configuration
- if _, hasPlaywright := tools["playwright"]; hasPlaywright {
- // Create playwright as an MCP tool with the same tools available as copilot agent
- playwrightMCP := map[string]any{
- "allowed": GetPlaywrightTools(),
+ if playwrightTool, hasPlaywright := tools["playwright"]; hasPlaywright {
+ playwrightConfig := parsePlaywrightTool(playwrightTool)
+ if !isPlaywrightCLIMode(playwrightConfig) {
+ // Create playwright as an MCP tool with the same tools available as copilot agent
+ playwrightMCP := map[string]any{
+ "allowed": GetPlaywrightTools(),
+ }
+ result["playwright"] = playwrightMCP
}
- result["playwright"] = playwrightMCP
}
// Update claude section
diff --git a/pkg/workflow/codex_engine.go b/pkg/workflow/codex_engine.go
index 8cfd960e341..cef4ad2bfd9 100644
--- a/pkg/workflow/codex_engine.go
+++ b/pkg/workflow/codex_engine.go
@@ -430,7 +430,7 @@ func (e *CodexEngine) expandNeutralToolsToCodexTools(toolsConfig *ToolsConfig) *
maps.Copy(result.raw, toolsConfig.raw)
// Handle playwright tool by converting it to an MCP tool configuration with copilot agent tools
- if toolsConfig.Playwright != nil {
+ if toolsConfig.Playwright != nil && !isPlaywrightCLIMode(toolsConfig.Playwright) {
// Create an updated Playwright config with the allowed tools
playwrightConfig := &PlaywrightToolConfig{
Version: toolsConfig.Playwright.Version,
diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go
index af882d0dddd..58d0c72fb5a 100644
--- a/pkg/workflow/compiler_orchestrator_workflow.go
+++ b/pkg/workflow/compiler_orchestrator_workflow.go
@@ -83,6 +83,11 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
return nil, fmt.Errorf("%s: %w", cleanPath, err)
}
+ // Validate playwright tool configuration
+ if err := validatePlaywrightToolConfig(workflowData.ParsedTools, workflowData.Name); err != nil {
+ return nil, fmt.Errorf("%s: %w", cleanPath, err)
+ }
+
// Validate GitHub tool configuration
if err := validateGitHubToolConfig(workflowData.ParsedTools, workflowData.Name); err != nil {
return nil, fmt.Errorf("%s: %w", cleanPath, err)
diff --git a/pkg/workflow/compiler_string_api.go b/pkg/workflow/compiler_string_api.go
index 7edd204a9ab..fe64991d2c0 100644
--- a/pkg/workflow/compiler_string_api.go
+++ b/pkg/workflow/compiler_string_api.go
@@ -131,6 +131,11 @@ func (c *Compiler) ParseWorkflowString(content string, virtualPath string) (*Wor
return nil, fmt.Errorf("%s: %w", cleanPath, err)
}
+ // Validate playwright tool configuration
+ if err := validatePlaywrightToolConfig(workflowData.ParsedTools, workflowData.Name); err != nil {
+ return nil, fmt.Errorf("%s: %w", cleanPath, err)
+ }
+
// Validate GitHub tool configuration
if err := validateGitHubToolConfig(workflowData.ParsedTools, workflowData.Name); err != nil {
return nil, fmt.Errorf("%s: %w", cleanPath, err)
diff --git a/pkg/workflow/compiler_yaml_main_job.go b/pkg/workflow/compiler_yaml_main_job.go
index 6f99154f9d8..7ccbea2bc80 100644
--- a/pkg/workflow/compiler_yaml_main_job.go
+++ b/pkg/workflow/compiler_yaml_main_job.go
@@ -259,6 +259,17 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat
}
}
+ // Add playwright-cli installation step if playwright is configured in CLI mode
+ if hasPlaywrightCLITool(data.ParsedTools) {
+ compilerYamlLog.Print("Adding playwright-cli installation step")
+ playwrightCLISteps := generatePlaywrightCLIInstallSteps(data.ParsedTools.Playwright)
+ for _, step := range playwrightCLISteps {
+ for _, line := range step {
+ yaml.WriteString(line + "\n")
+ }
+ }
+ }
+
// Add APM (Agent Package Manager) setup step if dependencies are specified
if data.APMDependencies != nil && len(data.APMDependencies.Packages) > 0 {
// Download the pre-packed APM bundle from the separate "apm" artifact.
diff --git a/pkg/workflow/docker.go b/pkg/workflow/docker.go
index c1a3ae10bd6..2c91bc2925b 100644
--- a/pkg/workflow/docker.go
+++ b/pkg/workflow/docker.go
@@ -30,12 +30,15 @@ func collectDockerImages(tools map[string]any, workflowData *WorkflowData, actio
}
}
- // Check for Playwright tool (uses Docker image - no version tag, only one image)
- if _, hasPlaywright := tools["playwright"]; hasPlaywright {
- image := "mcr.microsoft.com/playwright/mcp"
- if !imageSet[image] {
- images = append(images, image)
- imageSet[image] = true
+ // Check for Playwright tool (uses Docker image only in MCP mode)
+ if playwrightTool, hasPlaywright := tools["playwright"]; hasPlaywright {
+ playwrightConfig := parsePlaywrightTool(playwrightTool)
+ if !isPlaywrightCLIMode(playwrightConfig) {
+ image := "mcr.microsoft.com/playwright/mcp"
+ if !imageSet[image] {
+ images = append(images, image)
+ imageSet[image] = true
+ }
}
}
diff --git a/pkg/workflow/mcp_detection.go b/pkg/workflow/mcp_detection.go
index 7008656ee37..3c1f25214b6 100644
--- a/pkg/workflow/mcp_detection.go
+++ b/pkg/workflow/mcp_detection.go
@@ -19,9 +19,16 @@ func HasMCPServers(workflowData *WorkflowData) bool {
if toolValue == false {
continue
}
- if toolName == "github" || toolName == "playwright" || toolName == "cache-memory" || toolName == "agentic-workflows" {
+ if toolName == "github" || toolName == "cache-memory" || toolName == "agentic-workflows" {
return true
}
+ if toolName == "playwright" {
+ playwrightConfig := parsePlaywrightTool(toolValue)
+ if !isPlaywrightCLIMode(playwrightConfig) {
+ return true
+ }
+ continue
+ }
// Check for custom MCP tools
if mcpConfig, ok := toolValue.(map[string]any); ok {
if hasMcp, _ := hasMCPConfig(mcpConfig); hasMcp {
diff --git a/pkg/workflow/mcp_setup_generator.go b/pkg/workflow/mcp_setup_generator.go
index f9cc9af6eec..d0e0811a4f2 100644
--- a/pkg/workflow/mcp_setup_generator.go
+++ b/pkg/workflow/mcp_setup_generator.go
@@ -95,8 +95,14 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
continue
}
// Standard MCP tools
- if toolName == "github" || toolName == "playwright" || toolName == "serena" || toolName == "cache-memory" || toolName == "agentic-workflows" {
+ if toolName == "github" || toolName == "serena" || toolName == "cache-memory" || toolName == "agentic-workflows" {
mcpTools = append(mcpTools, toolName)
+ } else if toolName == "playwright" {
+ // Only add playwright to MCP tools if not in CLI mode
+ playwrightConfig := parsePlaywrightTool(toolValue)
+ if !isPlaywrightCLIMode(playwrightConfig) {
+ mcpTools = append(mcpTools, toolName)
+ }
} else if mcpConfig, ok := toolValue.(map[string]any); ok {
// Check if it's explicitly marked as MCP type in the new format
if hasMcp, _ := hasMCPConfig(mcpConfig); hasMcp {
diff --git a/pkg/workflow/playwright_cli_test.go b/pkg/workflow/playwright_cli_test.go
new file mode 100644
index 00000000000..104e6480341
--- /dev/null
+++ b/pkg/workflow/playwright_cli_test.go
@@ -0,0 +1,442 @@
+//go:build !integration
+
+package workflow
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/github/gh-aw/pkg/stringutil"
+)
+
+// TestIsPlaywrightCLIMode tests the isPlaywrightCLIMode helper function
+func TestIsPlaywrightCLIMode(t *testing.T) {
+ tests := []struct {
+ name string
+ config *PlaywrightToolConfig
+ expected bool
+ }{
+ {
+ name: "nil config is not CLI mode",
+ config: nil,
+ expected: false,
+ },
+ {
+ name: "empty config is not CLI mode",
+ config: &PlaywrightToolConfig{},
+ expected: false,
+ },
+ {
+ name: "mode mcp is not CLI mode",
+ config: &PlaywrightToolConfig{Mode: "mcp"},
+ expected: false,
+ },
+ {
+ name: "mode cli is CLI mode",
+ config: &PlaywrightToolConfig{Mode: "cli"},
+ expected: true,
+ },
+ {
+ name: "unknown mode is not CLI mode",
+ config: &PlaywrightToolConfig{Mode: "unknown"},
+ expected: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := isPlaywrightCLIMode(tt.config)
+ assert.Equal(t, tt.expected, result, "isPlaywrightCLIMode(%+v)", tt.config)
+ })
+ }
+}
+
+// TestParsePlaywrightToolWithMode tests that the mode field is parsed correctly
+func TestParsePlaywrightToolWithMode(t *testing.T) {
+ tests := []struct {
+ name string
+ input any
+ expectedMode string
+ }{
+ {
+ name: "nil input has empty mode",
+ input: nil,
+ expectedMode: "",
+ },
+ {
+ name: "mode cli is parsed",
+ input: map[string]any{
+ "mode": "cli",
+ },
+ expectedMode: "cli",
+ },
+ {
+ name: "mode mcp is parsed",
+ input: map[string]any{
+ "mode": "mcp",
+ },
+ expectedMode: "mcp",
+ },
+ {
+ name: "no mode field has empty mode",
+ input: map[string]any{
+ "version": "1.0.0",
+ },
+ expectedMode: "",
+ },
+ {
+ name: "mode with other fields is parsed",
+ input: map[string]any{
+ "mode": "cli",
+ "version": "1.0.0",
+ "args": []any{"--browser", "chromium"},
+ },
+ expectedMode: "cli",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ config := parsePlaywrightTool(tt.input)
+ require.NotNil(t, config, "parsePlaywrightTool should not return nil")
+ assert.Equal(t, tt.expectedMode, config.Mode, "Mode should be parsed correctly")
+ })
+ }
+}
+
+// TestValidatePlaywrightToolConfig tests playwright mode validation
+func TestValidatePlaywrightToolConfig(t *testing.T) {
+ tests := []struct {
+ name string
+ tools *Tools
+ expectErr bool
+ errMsg string
+ }{
+ {
+ name: "nil tools is valid",
+ tools: nil,
+ expectErr: false,
+ },
+ {
+ name: "nil playwright is valid",
+ tools: &Tools{
+ Custom: make(map[string]MCPServerConfig),
+ raw: make(map[string]any),
+ },
+ expectErr: false,
+ },
+ {
+ name: "mode cli is valid",
+ tools: &Tools{
+ Playwright: &PlaywrightToolConfig{Mode: "cli"},
+ Custom: make(map[string]MCPServerConfig),
+ raw: make(map[string]any),
+ },
+ expectErr: false,
+ },
+ {
+ name: "mode mcp is valid",
+ tools: &Tools{
+ Playwright: &PlaywrightToolConfig{Mode: "mcp"},
+ Custom: make(map[string]MCPServerConfig),
+ raw: make(map[string]any),
+ },
+ expectErr: false,
+ },
+ {
+ name: "empty mode is valid (defaults to mcp)",
+ tools: &Tools{
+ Playwright: &PlaywrightToolConfig{},
+ Custom: make(map[string]MCPServerConfig),
+ raw: make(map[string]any),
+ },
+ expectErr: false,
+ },
+ {
+ name: "invalid mode is rejected",
+ tools: &Tools{
+ Playwright: &PlaywrightToolConfig{Mode: "invalid"},
+ Custom: make(map[string]MCPServerConfig),
+ raw: make(map[string]any),
+ },
+ expectErr: true,
+ errMsg: "tools.playwright.mode",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := validatePlaywrightToolConfig(tt.tools, "test-workflow")
+ if tt.expectErr {
+ require.Error(t, err, "Expected an error but got none")
+ assert.Contains(t, err.Error(), tt.errMsg, "Error message should contain expected text")
+ } else {
+ assert.NoError(t, err, "Expected no error")
+ }
+ })
+ }
+}
+
+// TestPlaywrightCLIModeCompilation tests that playwright CLI mode compiles correctly
+func TestPlaywrightCLIModeCompilation(t *testing.T) {
+ tmpDir, err := os.MkdirTemp("", "gh-aw-playwright-cli-test-*")
+ require.NoError(t, err, "Failed to create temp dir")
+ defer os.RemoveAll(tmpDir)
+
+ tests := []struct {
+ name string
+ workflowContent string
+ shouldContain []string
+ shouldNotContain []string
+ }{
+ {
+ name: "CLI mode does not include playwright MCP Docker image",
+ workflowContent: `---
+on: push
+engine: claude
+tools:
+ playwright:
+ mode: cli
+---
+
+# Test Workflow
+
+Test playwright CLI mode.
+`,
+ shouldNotContain: []string{
+ "mcr.microsoft.com/playwright/mcp",
+ },
+ shouldContain: []string{
+ "playwright-cli",
+ "Install Playwright CLI",
+ },
+ },
+ {
+ name: "MCP mode (default) includes playwright MCP Docker image",
+ workflowContent: `---
+on: push
+engine: claude
+tools:
+ playwright:
+---
+
+# Test Workflow
+
+Test playwright MCP mode.
+`,
+ shouldContain: []string{
+ "mcr.microsoft.com/playwright/mcp",
+ },
+ shouldNotContain: []string{
+ "playwright-cli",
+ "Install Playwright CLI",
+ },
+ },
+ {
+ name: "Explicit MCP mode includes playwright MCP Docker image",
+ workflowContent: `---
+on: push
+engine: claude
+tools:
+ playwright:
+ mode: mcp
+---
+
+# Test Workflow
+
+Test playwright explicit MCP mode.
+`,
+ shouldContain: []string{
+ "mcr.microsoft.com/playwright/mcp",
+ },
+ shouldNotContain: []string{
+ "playwright-cli",
+ "Install Playwright CLI",
+ },
+ },
+ {
+ name: "CLI mode with copilot engine",
+ workflowContent: `---
+on: push
+engine: copilot
+tools:
+ playwright:
+ mode: cli
+---
+
+# Test Workflow
+
+Test playwright CLI mode with copilot.
+`,
+ shouldNotContain: []string{
+ "mcr.microsoft.com/playwright/mcp",
+ },
+ shouldContain: []string{
+ "playwright-cli",
+ "Install Playwright CLI",
+ },
+ },
+ {
+ name: "CLI mode with codex engine",
+ workflowContent: `---
+on: push
+engine: codex
+tools:
+ playwright:
+ mode: cli
+---
+
+# Test Workflow
+
+Test playwright CLI mode with codex.
+`,
+ shouldNotContain: []string{
+ "mcr.microsoft.com/playwright/mcp",
+ },
+ shouldContain: []string{
+ "playwright-cli",
+ "Install Playwright CLI",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ testFile := filepath.Join(tmpDir, "test-"+strings.ReplaceAll(tt.name, " ", "-")+".md")
+ err := os.WriteFile(testFile, []byte(tt.workflowContent), 0644)
+ require.NoError(t, err, "Failed to create test workflow file")
+
+ compiler := NewCompiler()
+ err = compiler.CompileWorkflow(testFile)
+ require.NoError(t, err, "Failed to compile workflow")
+
+ lockFile := stringutil.MarkdownToLockFile(testFile)
+ lockContent, err := os.ReadFile(lockFile)
+ require.NoError(t, err, "Failed to read generated lock file")
+
+ lockStr := string(lockContent)
+
+ for _, expected := range tt.shouldContain {
+ assert.Contains(t, lockStr, expected, "Lock file should contain: %s", expected)
+ }
+ for _, notExpected := range tt.shouldNotContain {
+ assert.NotContains(t, lockStr, notExpected, "Lock file should NOT contain: %s", notExpected)
+ }
+ })
+ }
+}
+
+// TestPlaywrightCLIModePrompt tests that the CLI mode prompt is included instead of MCP prompt
+func TestPlaywrightCLIModePrompt(t *testing.T) {
+ tmpDir, err := os.MkdirTemp("", "gh-aw-playwright-cli-prompt-test-*")
+ require.NoError(t, err, "Failed to create temp dir")
+ defer os.RemoveAll(tmpDir)
+
+ tests := []struct {
+ name string
+ workflowContent string
+ shouldContain []string
+ shouldNotContain []string
+ }{
+ {
+ name: "CLI mode uses playwright-cli prompt",
+ workflowContent: `---
+on: push
+engine: claude
+tools:
+ playwright:
+ mode: cli
+---
+
+# Test Workflow
+
+Test playwright CLI prompt.
+`,
+ shouldContain: []string{
+ "playwright_cli_prompt.md",
+ },
+ shouldNotContain: []string{
+ "playwright_prompt.md",
+ },
+ },
+ {
+ name: "MCP mode uses playwright MCP prompt",
+ workflowContent: `---
+on: push
+engine: claude
+tools:
+ playwright:
+---
+
+# Test Workflow
+
+Test playwright MCP prompt.
+`,
+ shouldContain: []string{
+ "playwright_prompt.md",
+ },
+ shouldNotContain: []string{
+ "playwright_cli_prompt.md",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ testFile := filepath.Join(tmpDir, "test-"+strings.ReplaceAll(tt.name, " ", "-")+".md")
+ err := os.WriteFile(testFile, []byte(tt.workflowContent), 0644)
+ require.NoError(t, err, "Failed to create test workflow file")
+
+ compiler := NewCompiler()
+ err = compiler.CompileWorkflow(testFile)
+ require.NoError(t, err, "Failed to compile workflow")
+
+ lockFile := stringutil.MarkdownToLockFile(testFile)
+ lockContent, err := os.ReadFile(lockFile)
+ require.NoError(t, err, "Failed to read generated lock file")
+
+ lockStr := string(lockContent)
+
+ for _, expected := range tt.shouldContain {
+ assert.Contains(t, lockStr, expected, "Lock file should contain: %s", expected)
+ }
+ for _, notExpected := range tt.shouldNotContain {
+ assert.NotContains(t, lockStr, notExpected, "Lock file should NOT contain: %s", notExpected)
+ }
+ })
+ }
+}
+
+// TestPlaywrightCLIModeValidationError tests that invalid mode values are rejected
+func TestPlaywrightCLIModeValidationError(t *testing.T) {
+ tmpDir, err := os.MkdirTemp("", "gh-aw-playwright-cli-validation-test-*")
+ require.NoError(t, err, "Failed to create temp dir")
+ defer os.RemoveAll(tmpDir)
+
+ workflowContent := `---
+on: push
+engine: claude
+tools:
+ playwright:
+ mode: invalid
+---
+
+# Test Workflow
+
+Test playwright invalid mode.
+`
+ testFile := filepath.Join(tmpDir, "test-invalid-mode.md")
+ err = os.WriteFile(testFile, []byte(workflowContent), 0644)
+ require.NoError(t, err, "Failed to create test workflow file")
+
+ compiler := NewCompiler()
+ err = compiler.CompileWorkflow(testFile)
+ require.Error(t, err, "Expected compilation to fail with invalid mode")
+ // Either the schema validator or our custom validator should reject the invalid mode
+ assert.Contains(t, err.Error(), "mode", "Error should mention the mode field")
+}
diff --git a/pkg/workflow/prompt_constants.go b/pkg/workflow/prompt_constants.go
index 642955b5927..80050908ae4 100644
--- a/pkg/workflow/prompt_constants.go
+++ b/pkg/workflow/prompt_constants.go
@@ -12,6 +12,7 @@ const (
prContextPromptFile = "pr_context_prompt.md"
tempFolderPromptFile = "temp_folder_prompt.md"
playwrightPromptFile = "playwright_prompt.md"
+ playwrightCLIPromptFile = "playwright_cli_prompt.md"
markdownPromptFile = "markdown.md"
xpiaPromptFile = "xpia.md"
cacheMemoryPromptFile = "cache_memory_prompt.md"
diff --git a/pkg/workflow/prompts.go b/pkg/workflow/prompts.go
index 00b9e7b132c..781dce90425 100644
--- a/pkg/workflow/prompts.go
+++ b/pkg/workflow/prompts.go
@@ -29,6 +29,14 @@ func hasPlaywrightTool(parsedTools *Tools) bool {
return hasPlaywright
}
+// hasPlaywrightCLITool checks if the playwright tool is enabled in CLI mode
+func hasPlaywrightCLITool(parsedTools *Tools) bool {
+ if parsedTools == nil {
+ return false
+ }
+ return isPlaywrightCLIMode(parsedTools.Playwright)
+}
+
// ============================================================================
// Tool Prompts - Agentic Workflows
// ============================================================================
diff --git a/pkg/workflow/runtime_step_generator.go b/pkg/workflow/runtime_step_generator.go
index e3ede0bea97..0b53fb290f8 100644
--- a/pkg/workflow/runtime_step_generator.go
+++ b/pkg/workflow/runtime_step_generator.go
@@ -63,6 +63,28 @@ func GenerateSerenaLanguageServiceSteps(tools *ToolsConfig) []GitHubActionStep {
return []GitHubActionStep{}
}
+// generatePlaywrightCLIInstallSteps creates installation steps for playwright-cli.
+// playwright-cli is installed directly on the runner when tools.playwright.mode is "cli".
+// If a version is specified in the config, it is used for a pinned installation.
+// See https://github.com/microsoft/playwright-cli for the CLI tool.
+func generatePlaywrightCLIInstallSteps(config *PlaywrightToolConfig) []GitHubActionStep {
+ runtimeStepGeneratorLog.Print("Generating playwright-cli installation steps")
+
+ packageSpec := "playwright-cli"
+ if config != nil && config.Version != "" {
+ packageSpec = "playwright-cli@" + config.Version
+ }
+
+ return []GitHubActionStep{
+ {
+ " - name: Install Playwright CLI",
+ " run: |",
+ " npm install -g " + packageSpec,
+ " playwright-cli install",
+ },
+ }
+}
+
// generateSetupStep creates a setup step for a given runtime requirement
func generateSetupStep(req *RuntimeRequirement) GitHubActionStep {
runtime := req.Runtime
diff --git a/pkg/workflow/tools_parser.go b/pkg/workflow/tools_parser.go
index 81305ecaa8b..0dada3a07e2 100644
--- a/pkg/workflow/tools_parser.go
+++ b/pkg/workflow/tools_parser.go
@@ -309,6 +309,11 @@ func parsePlaywrightTool(val any) *PlaywrightToolConfig {
config.Version = fmt.Sprintf("%g", versionNum)
}
+ // Handle mode field ("cli" or "mcp")
+ if mode, ok := configMap["mode"].(string); ok {
+ config.Mode = mode
+ }
+
// Handle args field - can be []any or []string
if argsValue, ok := configMap["args"]; ok {
if arr, ok := argsValue.([]any); ok {
diff --git a/pkg/workflow/tools_types.go b/pkg/workflow/tools_types.go
index 22f666171b3..bd31b90691c 100644
--- a/pkg/workflow/tools_types.go
+++ b/pkg/workflow/tools_types.go
@@ -304,9 +304,16 @@ type GitHubToolConfig struct {
// PlaywrightToolConfig represents the configuration for the Playwright tool
type PlaywrightToolConfig struct {
Version string `yaml:"version,omitempty"`
+ Mode string `yaml:"mode,omitempty"` // "cli" or "mcp" (default: "mcp")
Args []string `yaml:"args,omitempty"`
}
+// isPlaywrightCLIMode returns true if the playwright tool is configured to use CLI mode.
+// In CLI mode, playwright-cli is installed directly and no MCP server is started.
+func isPlaywrightCLIMode(config *PlaywrightToolConfig) bool {
+ return config != nil && config.Mode == "cli"
+}
+
// SerenaToolConfig represents the configuration for the Serena MCP tool
type SerenaToolConfig struct {
Version string `yaml:"version,omitempty"`
diff --git a/pkg/workflow/tools_validation.go b/pkg/workflow/tools_validation.go
index 343c18cc5a4..a05539b72d7 100644
--- a/pkg/workflow/tools_validation.go
+++ b/pkg/workflow/tools_validation.go
@@ -29,6 +29,23 @@ func validateBashToolConfig(tools *Tools, workflowName string) error {
return nil
}
+// validatePlaywrightToolConfig validates the playwright tool configuration.
+// It ensures the mode field, if set, has a valid value ("cli" or "mcp").
+// An empty or unset mode defaults to "mcp" behavior.
+func validatePlaywrightToolConfig(tools *Tools, workflowName string) error {
+ if tools == nil || tools.Playwright == nil {
+ return nil
+ }
+
+ mode := tools.Playwright.Mode
+ if mode != "" && mode != "cli" && mode != "mcp" {
+ toolsValidationLog.Printf("Invalid playwright mode in workflow: %s, mode=%s", workflowName, mode)
+ return fmt.Errorf("invalid playwright tool configuration: 'tools.playwright.mode' must be \"cli\" or \"mcp\", got %q", mode)
+ }
+
+ return nil
+}
+
// validateGitHubReadOnly validates that read-only: false is not set for the GitHub tool.
// The GitHub MCP server always operates in read-only mode; write access is not permitted.
func validateGitHubReadOnly(tools *Tools, workflowName string) error {
diff --git a/pkg/workflow/unified_prompt_step.go b/pkg/workflow/unified_prompt_step.go
index afabe94778d..e5aa65a9a64 100644
--- a/pkg/workflow/unified_prompt_step.go
+++ b/pkg/workflow/unified_prompt_step.go
@@ -248,11 +248,19 @@ func (c *Compiler) collectPromptSections(data *WorkflowData) []PromptSection {
// 3. Playwright instructions (if playwright tool is enabled)
if hasPlaywrightTool(data.ParsedTools) {
- unifiedPromptLog.Print("Adding playwright section")
- sections = append(sections, PromptSection{
- Content: playwrightPromptFile,
- IsFile: true,
- })
+ if hasPlaywrightCLITool(data.ParsedTools) {
+ unifiedPromptLog.Print("Adding playwright-cli section")
+ sections = append(sections, PromptSection{
+ Content: playwrightCLIPromptFile,
+ IsFile: true,
+ })
+ } else {
+ unifiedPromptLog.Print("Adding playwright section")
+ sections = append(sections, PromptSection{
+ Content: playwrightPromptFile,
+ IsFile: true,
+ })
+ }
}
// 4. Agentic Workflows MCP guide (if agentic-workflows tool is enabled)
From 1c00598ee2304eb59702f54b731ec5d4e9395ac6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 03:15:38 +0000
Subject: [PATCH 3/7] feat: Enable playwright cli mode in smoke-claude workflow
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/smoke-claude.lock.yml | 60 ++++---------------------
.github/workflows/smoke-claude.md | 3 +-
2 files changed, 11 insertions(+), 52 deletions(-)
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index ff6daf654bc..cb38585f546 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -34,7 +34,7 @@
#
# inlined-imports: true
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"9a4d79294c209d0051ffe00014b288b54a2be522a7908fd96f44fab6aa5c9e60","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"51cd87503667260666ec8f143cc6a814780fea304e105b6671722a35afa5dcbb","strict":true}
name: "Smoke Claude"
"on":
@@ -198,7 +198,7 @@ jobs:
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
- cat "${RUNNER_TEMP}/gh-aw/prompts/playwright_prompt.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/playwright_cli_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/agentic_workflows_guide.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
@@ -486,7 +486,7 @@ jobs:
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `__GH_AW_GITHUB_WORKSPACE__` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
- 5. **Playwright Testing**: Use the playwright tools to navigate to https://github.com and verify the page title contains "GitHub" (do NOT try to install playwright - use the provided MCP tools)
+ 5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-__GH_AW_GITHUB_RUN_ID__.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
@@ -811,6 +811,10 @@ jobs:
run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.24.3
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
+ - name: Install Playwright CLI
+ run: |
+ npm install -g playwright-cli
+ playwright-cli install
- name: Download APM bundle artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
@@ -832,7 +836,7 @@ jobs:
const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.3 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.3 ghcr.io/github/gh-aw-firewall/squid:0.24.3 ghcr.io/github/gh-aw-mcpg:v0.1.18 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest mcr.microsoft.com/playwright/mcp node:lts-alpine
+ run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.3 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.3 ghcr.io/github/gh-aw-firewall/squid:0.24.3 ghcr.io/github/gh-aw-mcpg:v0.1.18 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -1805,7 +1809,6 @@ jobs:
run: |
set -eo pipefail
mkdir -p /tmp/gh-aw/mcp-config
- mkdir -p /tmp/gh-aw/mcp-logs/playwright
# Export gateway environment variables for MCP config and gateway script
export MCP_GATEWAY_PORT="80"
@@ -1871,30 +1874,6 @@ jobs:
}
}
},
- "playwright": {
- "container": "mcr.microsoft.com/playwright/mcp",
- "args": [
- "--init",
- "--network",
- "host",
- "--security-opt",
- "seccomp=unconfined",
- "--ipc=host"
- ],
- "entrypointArgs": [
- "--output-dir",
- "/tmp/gh-aw/mcp-logs/playwright",
- "--no-sandbox"
- ],
- "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
- "guard-policies": {
- "write-sink": {
- "accept": [
- "*"
- ]
- }
- }
- },
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
@@ -2040,27 +2019,6 @@ jobs:
# - mcp__github__search_pull_requests
# - mcp__github__search_repositories
# - mcp__github__search_users
- # - mcp__playwright__browser_click
- # - mcp__playwright__browser_close
- # - mcp__playwright__browser_console_messages
- # - mcp__playwright__browser_drag
- # - mcp__playwright__browser_evaluate
- # - mcp__playwright__browser_file_upload
- # - mcp__playwright__browser_fill_form
- # - mcp__playwright__browser_handle_dialog
- # - mcp__playwright__browser_hover
- # - mcp__playwright__browser_install
- # - mcp__playwright__browser_navigate
- # - mcp__playwright__browser_navigate_back
- # - mcp__playwright__browser_network_requests
- # - mcp__playwright__browser_press_key
- # - mcp__playwright__browser_resize
- # - mcp__playwright__browser_select_option
- # - mcp__playwright__browser_snapshot
- # - mcp__playwright__browser_tabs
- # - mcp__playwright__browser_take_screenshot
- # - mcp__playwright__browser_type
- # - mcp__playwright__browser_wait_for
# - mcp__serena
# - mcp__tavily
timeout-minutes: 10
@@ -2069,7 +2027,7 @@ jobs:
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,mcp.tavily.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.3 --skip-pull --enable-api-proxy \
- -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --max-turns 100 --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for,mcp__serena,mcp__tavily'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --max-turns 100 --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__serena,mcp__tavily'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
BASH_DEFAULT_TIMEOUT_MS: 60000
diff --git a/.github/workflows/smoke-claude.md b/.github/workflows/smoke-claude.md
index 20c974ecf24..fb7ea7f4b1e 100644
--- a/.github/workflows/smoke-claude.md
+++ b/.github/workflows/smoke-claude.md
@@ -43,6 +43,7 @@ tools:
github:
toolsets: [repos, pull_requests]
playwright:
+ mode: cli
edit:
bash:
- "*"
@@ -128,7 +129,7 @@ timeout-minutes: 10
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `${{ github.workspace }}` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
-5. **Playwright Testing**: Use the playwright tools to navigate to https://github.com and verify the page title contains "GitHub" (do NOT try to install playwright - use the provided MCP tools)
+5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-${{ github.run_id }}.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
From 921abaccfb286972dc55fece664508560536567f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 19 Mar 2026 03:31:10 +0000
Subject: [PATCH 4/7] Add changeset [skip-ci]
---
.changeset/patch-playwright-mode-selection.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 .changeset/patch-playwright-mode-selection.md
diff --git a/.changeset/patch-playwright-mode-selection.md b/.changeset/patch-playwright-mode-selection.md
new file mode 100644
index 00000000000..fca508c6385
--- /dev/null
+++ b/.changeset/patch-playwright-mode-selection.md
@@ -0,0 +1,5 @@
+---
+"gh-aw": patch
+---
+
+Add `tools.playwright.mode` (`"mcp"` default or `"cli"`) to choose between Playwright MCP integration and direct `playwright-cli` usage.
From 41b1f4df9ddf592a8f32a58a48e640fd32b41404 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 03:40:49 +0000
Subject: [PATCH 5/7] fix: Use @playwright/cli instead of deprecated
playwright-cli package
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/smoke-claude.lock.yml | 8 ++++----
.github/workflows/smoke-claude.md | 2 +-
actions/setup/md/playwright_cli_prompt.md | 12 ++++++------
pkg/workflow/playwright_cli_test.go | 10 +++++-----
pkg/workflow/runtime_step_generator.go | 14 +++++++-------
5 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index cb38585f546..794ba0f3b89 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -34,7 +34,7 @@
#
# inlined-imports: true
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"51cd87503667260666ec8f143cc6a814780fea304e105b6671722a35afa5dcbb","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"1f8214cda87054c41516c65a0ccf80ac4c674a2acbff18a44cfc7c6236d3d01d","strict":true}
name: "Smoke Claude"
"on":
@@ -486,7 +486,7 @@ jobs:
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `__GH_AW_GITHUB_WORKSPACE__` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
- 5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
+ 5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-__GH_AW_GITHUB_RUN_ID__.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
@@ -813,8 +813,8 @@ jobs:
run: npm install -g @anthropic-ai/claude-code@latest
- name: Install Playwright CLI
run: |
- npm install -g playwright-cli
- playwright-cli install
+ npm install -g @playwright/cli
+ playwright install --with-deps chromium
- name: Download APM bundle artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
diff --git a/.github/workflows/smoke-claude.md b/.github/workflows/smoke-claude.md
index fb7ea7f4b1e..8c5ca1c9d71 100644
--- a/.github/workflows/smoke-claude.md
+++ b/.github/workflows/smoke-claude.md
@@ -129,7 +129,7 @@ timeout-minutes: 10
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `${{ github.workspace }}` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
-5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
+5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-${{ github.run_id }}.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
diff --git a/actions/setup/md/playwright_cli_prompt.md b/actions/setup/md/playwright_cli_prompt.md
index dd521a0e0b8..e9935012f05 100644
--- a/actions/setup/md/playwright_cli_prompt.md
+++ b/actions/setup/md/playwright_cli_prompt.md
@@ -1,14 +1,14 @@
-Playwright CLI is available for browser automation tasks. Use bash to run playwright-cli commands directly on the runner.
+Playwright CLI is available for browser automation tasks. Use bash to run playwright commands directly on the runner.
-playwright-cli is installed and available as a command-line tool. Use the bash tool to run it. Common usage patterns:
+playwright is installed and available as a command-line tool. Use the bash tool to run it. Common usage patterns:
-- Screenshot a URL: `playwright-cli screenshot https://example.com screenshot.png`
-- Open a browser interactively (for scripting): `playwright-cli open https://example.com`
-- Generate a test script: `playwright-cli codegen https://example.com`
+- Screenshot a URL: `playwright screenshot https://example.com screenshot.png`
+- Open a browser interactively (for scripting): `playwright open https://example.com`
+- Generate a test script: `playwright codegen https://example.com`
Output files should be saved to /tmp/gh-aw/ for access by subsequent steps.
-Refer to `playwright-cli --help` for the full list of commands and options available.
+Refer to `playwright --help` for the full list of commands and options available.
diff --git a/pkg/workflow/playwright_cli_test.go b/pkg/workflow/playwright_cli_test.go
index 104e6480341..3a20a252615 100644
--- a/pkg/workflow/playwright_cli_test.go
+++ b/pkg/workflow/playwright_cli_test.go
@@ -212,7 +212,7 @@ Test playwright CLI mode.
"mcr.microsoft.com/playwright/mcp",
},
shouldContain: []string{
- "playwright-cli",
+ "@playwright/cli",
"Install Playwright CLI",
},
},
@@ -233,7 +233,7 @@ Test playwright MCP mode.
"mcr.microsoft.com/playwright/mcp",
},
shouldNotContain: []string{
- "playwright-cli",
+ "@playwright/cli",
"Install Playwright CLI",
},
},
@@ -255,7 +255,7 @@ Test playwright explicit MCP mode.
"mcr.microsoft.com/playwright/mcp",
},
shouldNotContain: []string{
- "playwright-cli",
+ "@playwright/cli",
"Install Playwright CLI",
},
},
@@ -277,7 +277,7 @@ Test playwright CLI mode with copilot.
"mcr.microsoft.com/playwright/mcp",
},
shouldContain: []string{
- "playwright-cli",
+ "@playwright/cli",
"Install Playwright CLI",
},
},
@@ -299,7 +299,7 @@ Test playwright CLI mode with codex.
"mcr.microsoft.com/playwright/mcp",
},
shouldContain: []string{
- "playwright-cli",
+ "@playwright/cli",
"Install Playwright CLI",
},
},
diff --git a/pkg/workflow/runtime_step_generator.go b/pkg/workflow/runtime_step_generator.go
index 0b53fb290f8..9b0fd2ae34a 100644
--- a/pkg/workflow/runtime_step_generator.go
+++ b/pkg/workflow/runtime_step_generator.go
@@ -63,16 +63,16 @@ func GenerateSerenaLanguageServiceSteps(tools *ToolsConfig) []GitHubActionStep {
return []GitHubActionStep{}
}
-// generatePlaywrightCLIInstallSteps creates installation steps for playwright-cli.
-// playwright-cli is installed directly on the runner when tools.playwright.mode is "cli".
+// generatePlaywrightCLIInstallSteps creates installation steps for @playwright/cli.
+// @playwright/cli is installed directly on the runner when tools.playwright.mode is "cli".
// If a version is specified in the config, it is used for a pinned installation.
-// See https://github.com/microsoft/playwright-cli for the CLI tool.
+// See https://www.npmjs.com/package/@playwright/cli for the CLI tool.
func generatePlaywrightCLIInstallSteps(config *PlaywrightToolConfig) []GitHubActionStep {
- runtimeStepGeneratorLog.Print("Generating playwright-cli installation steps")
+ runtimeStepGeneratorLog.Print("Generating playwright CLI installation steps")
- packageSpec := "playwright-cli"
+ packageSpec := "@playwright/cli"
if config != nil && config.Version != "" {
- packageSpec = "playwright-cli@" + config.Version
+ packageSpec = "@playwright/cli@" + config.Version
}
return []GitHubActionStep{
@@ -80,7 +80,7 @@ func generatePlaywrightCLIInstallSteps(config *PlaywrightToolConfig) []GitHubAct
" - name: Install Playwright CLI",
" run: |",
" npm install -g " + packageSpec,
- " playwright-cli install",
+ " playwright install --with-deps chromium",
},
}
}
From 12c33c9af85ad3cc61052d019a023a51c857ae64 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 04:02:39 +0000
Subject: [PATCH 6/7] fix: Use playwright-cli binary name from @playwright/cli
package
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/smoke-claude.lock.yml | 6 +++---
.github/workflows/smoke-claude.md | 2 +-
actions/setup/md/playwright_cli_prompt.md | 12 ++++++------
pkg/workflow/runtime_step_generator.go | 2 +-
4 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index 794ba0f3b89..9786180bed3 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -34,7 +34,7 @@
#
# inlined-imports: true
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"1f8214cda87054c41516c65a0ccf80ac4c674a2acbff18a44cfc7c6236d3d01d","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"51cd87503667260666ec8f143cc6a814780fea304e105b6671722a35afa5dcbb","strict":true}
name: "Smoke Claude"
"on":
@@ -486,7 +486,7 @@ jobs:
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `__GH_AW_GITHUB_WORKSPACE__` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
- 5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
+ 5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-__GH_AW_GITHUB_RUN_ID__.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
@@ -814,7 +814,7 @@ jobs:
- name: Install Playwright CLI
run: |
npm install -g @playwright/cli
- playwright install --with-deps chromium
+ playwright-cli install --with-deps chromium
- name: Download APM bundle artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
diff --git a/.github/workflows/smoke-claude.md b/.github/workflows/smoke-claude.md
index 8c5ca1c9d71..fb7ea7f4b1e 100644
--- a/.github/workflows/smoke-claude.md
+++ b/.github/workflows/smoke-claude.md
@@ -129,7 +129,7 @@ timeout-minutes: 10
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `${{ github.workspace }}` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
-5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
+5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-${{ github.run_id }}.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
diff --git a/actions/setup/md/playwright_cli_prompt.md b/actions/setup/md/playwright_cli_prompt.md
index e9935012f05..dd521a0e0b8 100644
--- a/actions/setup/md/playwright_cli_prompt.md
+++ b/actions/setup/md/playwright_cli_prompt.md
@@ -1,14 +1,14 @@
-Playwright CLI is available for browser automation tasks. Use bash to run playwright commands directly on the runner.
+Playwright CLI is available for browser automation tasks. Use bash to run playwright-cli commands directly on the runner.
-playwright is installed and available as a command-line tool. Use the bash tool to run it. Common usage patterns:
+playwright-cli is installed and available as a command-line tool. Use the bash tool to run it. Common usage patterns:
-- Screenshot a URL: `playwright screenshot https://example.com screenshot.png`
-- Open a browser interactively (for scripting): `playwright open https://example.com`
-- Generate a test script: `playwright codegen https://example.com`
+- Screenshot a URL: `playwright-cli screenshot https://example.com screenshot.png`
+- Open a browser interactively (for scripting): `playwright-cli open https://example.com`
+- Generate a test script: `playwright-cli codegen https://example.com`
Output files should be saved to /tmp/gh-aw/ for access by subsequent steps.
-Refer to `playwright --help` for the full list of commands and options available.
+Refer to `playwright-cli --help` for the full list of commands and options available.
diff --git a/pkg/workflow/runtime_step_generator.go b/pkg/workflow/runtime_step_generator.go
index 9b0fd2ae34a..07a1b847904 100644
--- a/pkg/workflow/runtime_step_generator.go
+++ b/pkg/workflow/runtime_step_generator.go
@@ -80,7 +80,7 @@ func generatePlaywrightCLIInstallSteps(config *PlaywrightToolConfig) []GitHubAct
" - name: Install Playwright CLI",
" run: |",
" npm install -g " + packageSpec,
- " playwright install --with-deps chromium",
+ " playwright-cli install --with-deps chromium",
},
}
}
From f76a84f6bdd6b80cad9f9d6facf6beca1a08646b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 04:46:03 +0000
Subject: [PATCH 7/7] fix: playwright-cli install-browser firefox and correct
screenshot usage
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/smoke-claude.lock.yml | 12 +++++++++---
.github/workflows/smoke-claude.md | 8 +++++++-
actions/setup/md/playwright_cli_prompt.md | 21 +++++++++++++++++----
pkg/workflow/runtime_step_generator.go | 2 +-
4 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index 9786180bed3..608cae46915 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -34,7 +34,7 @@
#
# inlined-imports: true
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"51cd87503667260666ec8f143cc6a814780fea304e105b6671722a35afa5dcbb","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"548f1fadb7251c6e56ea1d4b606efb9fe62bf7ea60ae5a74558898cfb8afefb5","strict":true}
name: "Smoke Claude"
"on":
@@ -486,7 +486,13 @@ jobs:
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `__GH_AW_GITHUB_WORKSPACE__` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
- 5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
+ 5. **Playwright Testing**: Use bash to take a screenshot of https://github.com with playwright-cli and verify the command runs successfully:
+ ```bash
+ playwright-cli open --browser firefox https://github.com <<'EOF'
+ screenshot /tmp/gh-aw/smoke-github-screenshot.png
+ exit
+ EOF
+ ```
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-__GH_AW_GITHUB_RUN_ID__.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
@@ -814,7 +820,7 @@ jobs:
- name: Install Playwright CLI
run: |
npm install -g @playwright/cli
- playwright-cli install --with-deps chromium
+ playwright-cli install-browser firefox --with-deps
- name: Download APM bundle artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
diff --git a/.github/workflows/smoke-claude.md b/.github/workflows/smoke-claude.md
index fb7ea7f4b1e..78b3671aa54 100644
--- a/.github/workflows/smoke-claude.md
+++ b/.github/workflows/smoke-claude.md
@@ -129,7 +129,13 @@ timeout-minutes: 10
- Use the Serena MCP server tool `activate_project` to initialize the workspace at `${{ github.workspace }}` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the mcpscripts-go/mcpscripts-make tools from the go-make shared workflow)
- After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results
4. **Make Build Testing**: Use the `mcpscripts-make` tool to build the project (use args: "build") and verify it succeeds
-5. **Playwright Testing**: Use the playwright-cli bash tool to take a screenshot of https://github.com and verify the command runs successfully (run: `playwright-cli screenshot https://github.com /tmp/gh-aw/smoke-github-screenshot.png`)
+5. **Playwright Testing**: Use bash to take a screenshot of https://github.com with playwright-cli and verify the command runs successfully:
+ ```bash
+ playwright-cli open --browser firefox https://github.com <<'EOF'
+ screenshot /tmp/gh-aw/smoke-github-screenshot.png
+ exit
+ EOF
+ ```
6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item
7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-claude-${{ github.run_id }}.txt` with content "Smoke test passed for Claude at $(date)" (create the directory if it doesn't exist)
8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
diff --git a/actions/setup/md/playwright_cli_prompt.md b/actions/setup/md/playwright_cli_prompt.md
index dd521a0e0b8..16d6012330e 100644
--- a/actions/setup/md/playwright_cli_prompt.md
+++ b/actions/setup/md/playwright_cli_prompt.md
@@ -1,14 +1,27 @@
Playwright CLI is available for browser automation tasks. Use bash to run playwright-cli commands directly on the runner.
-playwright-cli is installed and available as a command-line tool. Use the bash tool to run it. Common usage patterns:
+playwright-cli is a session-based tool. You must open a browser session first, then run commands within that session.
-- Screenshot a URL: `playwright-cli screenshot https://example.com screenshot.png`
-- Open a browser interactively (for scripting): `playwright-cli open https://example.com`
-- Generate a test script: `playwright-cli codegen https://example.com`
+To take a screenshot, pipe commands to `playwright-cli open` using a heredoc or echo:
+
+```bash
+playwright-cli open --browser firefox https://example.com <<'EOF'
+screenshot /tmp/gh-aw/screenshot.png
+exit
+EOF
+```
+
+Other commands available within a session (pipe after `open`):
+- `goto ` — navigate to a URL
+- `snapshot` — capture accessibility snapshot
+- `click [` — click an element
+- `screenshot [ref]` — screenshot of current page or element
Output files should be saved to /tmp/gh-aw/ for access by subsequent steps.
+Use `--browser firefox` (the default installed browser). Chromium requires SUID sandbox setup not available on standard runners.
+
Refer to `playwright-cli --help` for the full list of commands and options available.
]
diff --git a/pkg/workflow/runtime_step_generator.go b/pkg/workflow/runtime_step_generator.go
index 07a1b847904..29c9674698e 100644
--- a/pkg/workflow/runtime_step_generator.go
+++ b/pkg/workflow/runtime_step_generator.go
@@ -80,7 +80,7 @@ func generatePlaywrightCLIInstallSteps(config *PlaywrightToolConfig) []GitHubAct
" - name: Install Playwright CLI",
" run: |",
" npm install -g " + packageSpec,
- " playwright-cli install --with-deps chromium",
+ " playwright-cli install-browser firefox --with-deps",
},
}
}