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", }, } }