specweave 1.0.239 → 1.0.240

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CLAUDE.md +32 -30
  2. package/README.md +1 -1
  3. package/bin/specweave.js +16 -0
  4. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts +1 -0
  5. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts.map +1 -1
  6. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js +7 -3
  7. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js.map +1 -1
  8. package/dist/plugins/specweave-testing/lib/playwright-cli-runner.d.ts.map +1 -1
  9. package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js +27 -19
  10. package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js.map +1 -1
  11. package/dist/src/cli/commands/init.d.ts.map +1 -1
  12. package/dist/src/cli/commands/init.js +8 -0
  13. package/dist/src/cli/commands/init.js.map +1 -1
  14. package/dist/src/cli/commands/team.d.ts +20 -0
  15. package/dist/src/cli/commands/team.d.ts.map +1 -0
  16. package/dist/src/cli/commands/team.js +98 -0
  17. package/dist/src/cli/commands/team.js.map +1 -0
  18. package/dist/src/cli/helpers/init/claude-settings-env.d.ts +16 -0
  19. package/dist/src/cli/helpers/init/claude-settings-env.d.ts.map +1 -0
  20. package/dist/src/cli/helpers/init/claude-settings-env.js +44 -0
  21. package/dist/src/cli/helpers/init/claude-settings-env.js.map +1 -0
  22. package/dist/src/sync/engine.d.ts.map +1 -1
  23. package/dist/src/sync/engine.js +2 -0
  24. package/dist/src/sync/engine.js.map +1 -1
  25. package/dist/src/sync/providers/ado.d.ts.map +1 -1
  26. package/dist/src/sync/providers/ado.js +4 -2
  27. package/dist/src/sync/providers/ado.js.map +1 -1
  28. package/dist/src/sync/providers/github.d.ts.map +1 -1
  29. package/dist/src/sync/providers/github.js +11 -0
  30. package/dist/src/sync/providers/github.js.map +1 -1
  31. package/dist/src/sync/providers/jira.d.ts.map +1 -1
  32. package/dist/src/sync/providers/jira.js +14 -2
  33. package/dist/src/sync/providers/jira.js.map +1 -1
  34. package/package.json +2 -2
  35. package/plugins/specweave/hooks/user-prompt-submit.sh +16 -0
  36. package/plugins/specweave/scripts/read-grill-context.sh +149 -0
  37. package/plugins/specweave-testing/commands/e2e-setup.md +12 -12
  38. package/plugins/specweave-testing/commands/ui-automate.md +3 -3
  39. package/plugins/specweave-testing/commands/ui-inspect.md +3 -3
  40. package/plugins/specweave-testing/lib/playwright-cli-detector.js +8 -3
  41. package/plugins/specweave-testing/lib/playwright-cli-detector.ts +8 -3
  42. package/plugins/specweave-testing/lib/playwright-cli-runner.js +25 -20
  43. package/plugins/specweave-testing/lib/playwright-cli-runner.ts +24 -19
  44. package/plugins/specweave-testing/skills/e2e-testing/SKILL.md +29 -28
  45. package/plugins/specweave-testing/skills/qa-engineer/SKILL.md +2 -0
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env bash
2
+ # read-grill-context.sh - Load increment context for /sw:grill code review
3
+ #
4
+ # Executed by UserPromptSubmit hook when /sw:grill is detected.
5
+ # Outputs structured context + review instructions that get injected
6
+ # into the LLM's conversation via additionalContext.
7
+ #
8
+ # Usage: bash read-grill-context.sh [incrementId]
9
+ #
10
+ # If no incrementId given, auto-detects from active-increment.json.
11
+ # Supports partial ID matching (e.g., "0042" matches "0042-auth-feature").
12
+ #
13
+ # Compatible with bash 3.x (macOS default)
14
+
15
+ set -e
16
+
17
+ INCREMENT_ID="${1:-}"
18
+
19
+ # Find project root
20
+ PROJECT_ROOT="$PWD"
21
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
22
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
23
+ done
24
+
25
+ if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
26
+ echo "No SpecWeave project found (missing .specweave/)"
27
+ exit 1
28
+ fi
29
+
30
+ INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
31
+ STATE_DIR="$PROJECT_ROOT/.specweave/state"
32
+
33
+ # Auto-detect active increment if no ID given
34
+ if [[ -z "$INCREMENT_ID" ]]; then
35
+ if [[ -f "$STATE_DIR/active-increment.json" ]] && command -v jq >/dev/null 2>&1; then
36
+ INCREMENT_ID=$(jq -r '.ids[0] // empty' "$STATE_DIR/active-increment.json" 2>/dev/null)
37
+ fi
38
+
39
+ if [[ -z "$INCREMENT_ID" ]]; then
40
+ echo "No increment ID provided and no active increment found."
41
+ echo "Usage: /sw:grill <incrementId>"
42
+ exit 1
43
+ fi
44
+ fi
45
+
46
+ # Find increment folder (exact or partial match)
47
+ FOUND_DIR=""
48
+ if [[ -d "$INCREMENTS_DIR/$INCREMENT_ID" ]]; then
49
+ FOUND_DIR="$INCREMENTS_DIR/$INCREMENT_ID"
50
+ else
51
+ # Partial match
52
+ for dir in "$INCREMENTS_DIR"/[0-9]*/; do
53
+ dirname=$(basename "$dir")
54
+ if [[ "$dirname" == "$INCREMENT_ID"* ]]; then
55
+ FOUND_DIR="${dir%/}" # Strip trailing slash from glob
56
+ INCREMENT_ID="$dirname"
57
+ break
58
+ fi
59
+ done
60
+ fi
61
+
62
+ if [[ -z "$FOUND_DIR" ]] || [[ ! -d "$FOUND_DIR" ]]; then
63
+ echo "Increment not found: ${1:-$INCREMENT_ID}"
64
+ exit 1
65
+ fi
66
+
67
+ # Read metadata
68
+ STATUS="unknown"
69
+ TYPE="feature"
70
+ PRIORITY="P1"
71
+ if [[ -f "$FOUND_DIR/metadata.json" ]] && command -v jq >/dev/null 2>&1; then
72
+ STATUS=$(jq -r '.status // "unknown"' "$FOUND_DIR/metadata.json" 2>/dev/null)
73
+ TYPE=$(jq -r '.type // "feature"' "$FOUND_DIR/metadata.json" 2>/dev/null)
74
+ PRIORITY=$(jq -r '.priority // "P1"' "$FOUND_DIR/metadata.json" 2>/dev/null)
75
+ fi
76
+
77
+ # Count tasks
78
+ # NOTE: grep -c exits 1 on zero matches but still outputs "0".
79
+ # Using || true prevents set -e from killing the script without doubling output.
80
+ TASK_TOTAL=0
81
+ TASK_COMPLETED=0
82
+ TASK_PCT=0
83
+ if [[ -f "$FOUND_DIR/tasks.md" ]]; then
84
+ TASK_TOTAL=$(grep -c '### T-[0-9]' "$FOUND_DIR/tasks.md" 2>/dev/null) || true
85
+ TASK_COMPLETED=$(grep -ci '\*\*Status\*\*:\s*\[x\]' "$FOUND_DIR/tasks.md" 2>/dev/null) || true
86
+ [[ -z "$TASK_TOTAL" ]] && TASK_TOTAL=0
87
+ [[ -z "$TASK_COMPLETED" ]] && TASK_COMPLETED=0
88
+ if [[ "$TASK_TOTAL" -gt 0 ]]; then
89
+ TASK_PCT=$((TASK_COMPLETED * 100 / TASK_TOTAL))
90
+ fi
91
+ fi
92
+
93
+ # Count ACs from spec.md
94
+ AC_TOTAL=0
95
+ AC_COMPLETED=0
96
+ AC_PCT=0
97
+ if [[ -f "$FOUND_DIR/spec.md" ]]; then
98
+ AC_TOTAL=$(grep -cE '^\s*- \[[ x]\] \*\*AC-' "$FOUND_DIR/spec.md" 2>/dev/null) || true
99
+ AC_COMPLETED=$(grep -cE '^\s*- \[x\] \*\*AC-' "$FOUND_DIR/spec.md" 2>/dev/null) || true
100
+ [[ -z "$AC_TOTAL" ]] && AC_TOTAL=0
101
+ [[ -z "$AC_COMPLETED" ]] && AC_COMPLETED=0
102
+ if [[ "$AC_TOTAL" -gt 0 ]]; then
103
+ AC_PCT=$((AC_COMPLETED * 100 / AC_TOTAL))
104
+ fi
105
+ fi
106
+
107
+ # Output structured grill context
108
+ cat <<GRILL_CONTEXT
109
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
110
+ GRILL MODE ACTIVATED — ${INCREMENT_ID}
111
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
112
+
113
+ CONTEXT:
114
+ Status: ${STATUS} | Type: ${TYPE} | Priority: ${PRIORITY}
115
+ Tasks: ${TASK_COMPLETED}/${TASK_TOTAL} (${TASK_PCT}%)
116
+ ACs: ${AC_COMPLETED}/${AC_TOTAL} (${AC_PCT}%)
117
+
118
+ INCREMENT FILES:
119
+ spec.md: ${FOUND_DIR}/spec.md
120
+ tasks.md: ${FOUND_DIR}/tasks.md
121
+ plan.md: ${FOUND_DIR}/plan.md
122
+
123
+ INSTRUCTIONS — You are now in GRILL MODE. Act as a demanding senior engineer:
124
+
125
+ 1. Read the increment's spec.md to understand WHAT should have been built
126
+ 2. Read tasks.md to understand WHAT was done
127
+ 3. Find all changed files:
128
+ git diff --name-only \$(git merge-base HEAD main 2>/dev/null || git merge-base HEAD develop 2>/dev/null || echo HEAD~10)..HEAD 2>/dev/null
129
+ If git is not available, read the tasks for file references.
130
+ 4. For EACH significant changed file, check:
131
+ - Correctness: Does it satisfy the ACs? Edge cases? Null handling?
132
+ - Security: Injection? XSS? Auth bypass? Secrets in code?
133
+ - Performance: O(n²)? N+1 queries? Memory leaks? Blocking ops?
134
+ - Maintainability: God functions? Magic numbers? Inconsistent patterns?
135
+ 5. Categorize every issue found:
136
+ BLOCKER — Production will break, MUST fix
137
+ CRITICAL — Security/data risk, MUST fix
138
+ MAJOR — Significant gap, should fix
139
+ MINOR — Code quality, can fix later
140
+ SUGGESTION — Nice to have improvement
141
+
142
+ OUTPUT FORMAT:
143
+ For each issue: [{SEVERITY}] Title | File:line | Problem | Fix
144
+ End with: VERDICT: PASS (no blockers/criticals) or FAIL (has blockers/criticals)
145
+
146
+ If FAIL: List blocking issues and stop. Do NOT proceed with /sw:done.
147
+ If PASS: Code is ready for closure.
148
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
149
+ GRILL_CONTEXT
@@ -8,22 +8,22 @@ Set up comprehensive Playwright E2E testing with best practices, page objects, a
8
8
 
9
9
  You are an expert E2E testing engineer who implements production-ready Playwright test suites.
10
10
 
11
- ## CLI vs MCP Mode
11
+ ## CLI-First Rule (MANDATORY)
12
12
 
13
- SpecWeave supports two modes for Playwright browser automation:
13
+ **ALWAYS use Playwright CLI** (`npx playwright test`, `npx playwright codegen`) for all test setup, execution, and debugging. **DO NOT** use MCP Playwright tools (`browser_click`, `browser_snapshot`, `browser_navigate`, etc.) — they bypass playwright.config.ts, ignore fixtures/reporters, and waste tokens.
14
14
 
15
- - **@playwright/cli** (recommended for test execution): Token-efficient, file-based output, CI-friendly
16
- - **@playwright/mcp** (for interactive exploration): Rich inline snapshots, good for debugging
15
+ MCP Playwright is only acceptable for `/sw-testing:ui-inspect` (interactive DOM inspection). For everything else, use the CLI via Bash.
17
16
 
18
- Install CLI mode: `npm install -g @playwright/cli@latest`
17
+ ```bash
18
+ # Install Playwright
19
+ npm init playwright@latest
19
20
 
20
- Configure preference in `.specweave/config.json`:
21
- ```json
22
- {
23
- "testing": {
24
- "playwright": { "preferCli": true }
25
- }
26
- }
21
+ # Run tests
22
+ npx playwright test
23
+
24
+ # Debug
25
+ npx playwright test --debug
26
+ npx playwright test --ui
27
27
  ```
28
28
 
29
29
  ## Your Task
@@ -19,9 +19,9 @@ Create and execute automated browser workflows using Playwright. Generate script
19
19
  3. **Error Handling**: Add retry logic, timeouts, and fallbacks
20
20
  4. **Output Collection**: Capture screenshots, data, and validation results
21
21
 
22
- > **Why Code-First?** Anthropic research shows [code execution beats MCP tool calls](https://www.anthropic.com/engineering/code-execution-with-mcp) with 98% token reduction. Playwright code is reusable, committable, CI-runnable, and deterministic.
23
-
24
- > **CLI Mode Available**: When `@playwright/cli` is installed, this command routes to the CLI by default for maximum token efficiency (~250 chars per interaction vs ~5K+ via MCP). The CLI keeps browser state external and returns file references instead of inline DOM trees. Install: `npm install -g @playwright/cli@latest`
22
+ > **Code-First, CLI-First (MANDATORY)**: Generate Playwright scripts as `.ts` files and run them via CLI (`npx playwright test` or `npx tsx script.ts`). **DO NOT** use MCP Playwright tools (`browser_click`, `browser_fill_form`, `browser_navigate`, etc.) for automation — they produce ephemeral tool calls instead of reusable code, consume 20x more tokens, and cannot be committed or run in CI.
23
+ >
24
+ > Anthropic research confirms [code execution beats MCP tool calls](https://www.anthropic.com/engineering/code-execution-with-mcp) with 98% token reduction.
25
25
 
26
26
  ## Workflow Types
27
27
 
@@ -65,11 +65,11 @@ Provides:
65
65
 
66
66
  ## Browser Mode
67
67
 
68
- This command **prefers MCP mode** for rich DOM introspection. When the Playwright MCP plugin provides inline accessibility tree snapshots, the AI can reason about page structure and find optimal selectors.
68
+ This command is the **only SpecWeave testing command where MCP Playwright is appropriate**. It uses MCP's `browser_snapshot` for rich DOM introspection the AI can reason about the accessibility tree inline and find optimal selectors.
69
69
 
70
- If MCP is unavailable, falls back to `@playwright/cli snapshot` which saves the accessibility tree to a file (`.playwright-cli/*.yml`). The AI can then read the file, but loses the ability to iterate on snapshots without re-reading.
70
+ If MCP is unavailable, falls back to `@playwright/cli snapshot` which saves the accessibility tree to a file (`.playwright-cli/*.yml`).
71
71
 
72
- **Recommended**: Keep the Playwright MCP plugin installed for best `ui-inspect` experience.
72
+ **Important**: MCP Playwright is appropriate here for interactive inspection. For all other testing tasks (running tests, writing test code, automation scripts), always use the Playwright CLI via Bash instead. See `/sw-testing:e2e-setup` for details.
73
73
 
74
74
  ## Requirements
75
75
 
@@ -1,14 +1,18 @@
1
- import { execSync } from "child_process";
1
+ import { execFileSync } from "child_process";
2
2
  let cachedResult = null;
3
+ function clearCache() {
4
+ cachedResult = null;
5
+ }
3
6
  function detectPlaywrightCli(options = {}) {
4
7
  const { useCache = false } = options;
5
8
  if (useCache && cachedResult) {
6
9
  return cachedResult;
7
10
  }
11
+ const whichCmd = process.platform === "win32" ? "where" : "which";
8
12
  let path;
9
13
  let version;
10
14
  try {
11
- path = execSync("which playwright-cli", {
15
+ path = execFileSync(whichCmd, ["playwright-cli"], {
12
16
  encoding: "utf-8",
13
17
  timeout: 5e3
14
18
  }).trim();
@@ -18,7 +22,7 @@ function detectPlaywrightCli(options = {}) {
18
22
  return result2;
19
23
  }
20
24
  try {
21
- version = execSync("playwright-cli --version", {
25
+ version = execFileSync("playwright-cli", ["--version"], {
22
26
  encoding: "utf-8",
23
27
  timeout: 5e3
24
28
  }).trim();
@@ -29,5 +33,6 @@ function detectPlaywrightCli(options = {}) {
29
33
  return result;
30
34
  }
31
35
  export {
36
+ clearCache,
32
37
  detectPlaywrightCli
33
38
  };
@@ -1,4 +1,4 @@
1
- import { execSync } from 'child_process';
1
+ import { execFileSync } from 'child_process';
2
2
 
3
3
  export interface CliDetectionResult {
4
4
  installed: boolean;
@@ -12,6 +12,10 @@ export interface DetectOptions {
12
12
 
13
13
  let cachedResult: CliDetectionResult | null = null;
14
14
 
15
+ export function clearCache(): void {
16
+ cachedResult = null;
17
+ }
18
+
15
19
  export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionResult {
16
20
  const { useCache = false } = options;
17
21
 
@@ -19,11 +23,12 @@ export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionRe
19
23
  return cachedResult;
20
24
  }
21
25
 
26
+ const whichCmd = process.platform === 'win32' ? 'where' : 'which';
22
27
  let path: string | undefined;
23
28
  let version: string | undefined;
24
29
 
25
30
  try {
26
- path = execSync('which playwright-cli', {
31
+ path = execFileSync(whichCmd, ['playwright-cli'], {
27
32
  encoding: 'utf-8',
28
33
  timeout: 5_000,
29
34
  }).trim();
@@ -34,7 +39,7 @@ export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionRe
34
39
  }
35
40
 
36
41
  try {
37
- version = execSync('playwright-cli --version', {
42
+ version = execFileSync('playwright-cli', ['--version'], {
38
43
  encoding: 'utf-8',
39
44
  timeout: 5_000,
40
45
  }).trim();
@@ -1,21 +1,24 @@
1
- import { execSync } from "child_process";
1
+ import { execFileSync } from "child_process";
2
2
  class PlaywrightCliRunner {
3
3
  constructor(config = {}) {
4
4
  this.config = {
5
5
  headed: config.headed ?? false,
6
6
  browser: config.browser ?? "chrome",
7
- session: config.session,
8
7
  timeout: config.timeout ?? 3e4,
9
- ...config
8
+ session: config.session
10
9
  };
11
10
  }
12
- exec(command) {
13
- const sessionFlag = this.config.session ? `-s=${this.config.session} ` : "";
14
- const fullCommand = `playwright-cli ${sessionFlag}${command}`;
11
+ exec(args) {
12
+ const fullArgs = [];
13
+ if (this.config.session) {
14
+ fullArgs.push(`-s=${this.config.session}`);
15
+ }
16
+ fullArgs.push(...args);
15
17
  try {
16
- const output = execSync(fullCommand, {
18
+ const output = execFileSync("playwright-cli", fullArgs, {
17
19
  encoding: "utf-8",
18
- timeout: this.config.timeout
20
+ timeout: this.config.timeout,
21
+ maxBuffer: 1024 * 1024
19
22
  }).trim();
20
23
  return { ok: true, output };
21
24
  } catch (e) {
@@ -23,34 +26,36 @@ class PlaywrightCliRunner {
23
26
  }
24
27
  }
25
28
  open(url) {
26
- const headedFlag = this.config.headed ? " --headed" : "";
27
- const urlPart = url ? ` ${url}` : "";
28
- return this.exec(`open${urlPart}${headedFlag}`);
29
+ const args = ["open"];
30
+ if (url) args.push(url);
31
+ if (this.config.headed) args.push("--headed");
32
+ return this.exec(args);
29
33
  }
30
34
  navigate(url) {
31
- return this.exec(`goto ${url}`);
35
+ return this.exec(["goto", url]);
32
36
  }
33
37
  snapshot() {
34
- return this.exec("snapshot");
38
+ return this.exec(["snapshot"]);
35
39
  }
36
40
  screenshot(filename) {
37
- const flag = filename ? ` --filename ${filename}` : "";
38
- return this.exec(`screenshot${flag}`);
41
+ const args = ["screenshot"];
42
+ if (filename) args.push("--filename", filename);
43
+ return this.exec(args);
39
44
  }
40
45
  close() {
41
- return this.exec("close");
46
+ return this.exec(["close"]);
42
47
  }
43
48
  click(ref) {
44
- return this.exec(`click ${ref}`);
49
+ return this.exec(["click", ref]);
45
50
  }
46
51
  type(text) {
47
- return this.exec(`type "${text}"`);
52
+ return this.exec(["type", text]);
48
53
  }
49
54
  fill(ref, text) {
50
- return this.exec(`fill ${ref} "${text}"`);
55
+ return this.exec(["fill", ref, text]);
51
56
  }
52
57
  evaluate(fn) {
53
- return this.exec(`eval "${fn}"`);
58
+ return this.exec(["eval", fn]);
54
59
  }
55
60
  }
56
61
  export {
@@ -1,4 +1,4 @@
1
- import { execSync } from 'child_process';
1
+ import { execFileSync } from 'child_process';
2
2
 
3
3
  export interface CliRunnerConfig {
4
4
  headed?: boolean;
@@ -19,19 +19,22 @@ export class PlaywrightCliRunner {
19
19
  this.config = {
20
20
  headed: config.headed ?? false,
21
21
  browser: config.browser ?? 'chrome',
22
- session: config.session,
23
22
  timeout: config.timeout ?? 30_000,
24
- ...config,
23
+ session: config.session,
25
24
  };
26
25
  }
27
26
 
28
- private exec(command: string): CliResult {
29
- const sessionFlag = this.config.session ? `-s=${this.config.session} ` : '';
30
- const fullCommand = `playwright-cli ${sessionFlag}${command}`;
27
+ private exec(args: string[]): CliResult {
28
+ const fullArgs: string[] = [];
29
+ if (this.config.session) {
30
+ fullArgs.push(`-s=${this.config.session}`);
31
+ }
32
+ fullArgs.push(...args);
31
33
  try {
32
- const output = execSync(fullCommand, {
34
+ const output = execFileSync('playwright-cli', fullArgs, {
33
35
  encoding: 'utf-8',
34
36
  timeout: this.config.timeout,
37
+ maxBuffer: 1024 * 1024,
35
38
  }).trim();
36
39
  return { ok: true, output };
37
40
  } catch (e: unknown) {
@@ -40,41 +43,43 @@ export class PlaywrightCliRunner {
40
43
  }
41
44
 
42
45
  open(url?: string): CliResult {
43
- const headedFlag = this.config.headed ? ' --headed' : '';
44
- const urlPart = url ? ` ${url}` : '';
45
- return this.exec(`open${urlPart}${headedFlag}`);
46
+ const args = ['open'];
47
+ if (url) args.push(url);
48
+ if (this.config.headed) args.push('--headed');
49
+ return this.exec(args);
46
50
  }
47
51
 
48
52
  navigate(url: string): CliResult {
49
- return this.exec(`goto ${url}`);
53
+ return this.exec(['goto', url]);
50
54
  }
51
55
 
52
56
  snapshot(): CliResult {
53
- return this.exec('snapshot');
57
+ return this.exec(['snapshot']);
54
58
  }
55
59
 
56
60
  screenshot(filename?: string): CliResult {
57
- const flag = filename ? ` --filename ${filename}` : '';
58
- return this.exec(`screenshot${flag}`);
61
+ const args = ['screenshot'];
62
+ if (filename) args.push('--filename', filename);
63
+ return this.exec(args);
59
64
  }
60
65
 
61
66
  close(): CliResult {
62
- return this.exec('close');
67
+ return this.exec(['close']);
63
68
  }
64
69
 
65
70
  click(ref: string): CliResult {
66
- return this.exec(`click ${ref}`);
71
+ return this.exec(['click', ref]);
67
72
  }
68
73
 
69
74
  type(text: string): CliResult {
70
- return this.exec(`type "${text}"`);
75
+ return this.exec(['type', text]);
71
76
  }
72
77
 
73
78
  fill(ref: string, text: string): CliResult {
74
- return this.exec(`fill ${ref} "${text}"`);
79
+ return this.exec(['fill', ref, text]);
75
80
  }
76
81
 
77
82
  evaluate(fn: string): CliResult {
78
- return this.exec(`eval "${fn}"`);
83
+ return this.exec(['eval', fn]);
79
84
  }
80
85
  }
@@ -230,43 +230,44 @@ e2e/
230
230
  └── playwright.config.ts
231
231
  ```
232
232
 
233
- ## Browser Automation Mode: CLI vs MCP
233
+ ## Browser Automation: CLI-First Rule
234
234
 
235
- SpecWeave provides dual-mode browser automation for optimal token efficiency:
235
+ **MANDATORY**: Always use Playwright CLI (`npx playwright test`, Bash tool) for test execution, automation scripts, and CI/CD workflows. **DO NOT** use MCP Playwright tools (`browser_click`, `browser_snapshot`, `browser_navigate`, etc.) for these tasks.
236
236
 
237
- | Mode | Tool | Best For | Token Cost |
238
- |------|------|----------|------------|
239
- | **CLI** | `@playwright/cli` (Bash) | Test execution, automation, CI/CD | ~250 chars/action |
240
- | **MCP** | Playwright MCP plugin | Interactive inspection, self-healing | ~5K+ chars/action |
237
+ MCP Playwright tools consume ~20x more tokens per action and bypass your test configuration (playwright.config.ts, fixtures, reporters). They are the wrong tool for testing.
241
238
 
242
- ### When to Use CLI Mode
243
- - Running E2E test suites (`npx playwright test`)
244
- - Generating automation scripts
245
- - CI/CD pipelines (headless by default)
246
- - Token-constrained sessions
247
- - Network mocking and auth state management
239
+ ### When to Use What
248
240
 
249
- ### When to Use MCP Mode
250
- - Interactive page exploration and debugging
251
- - Self-healing test repair (needs full DOM reasoning)
252
- - Element inspection with accessibility tree
241
+ | Task | Tool | Why |
242
+ |------|------|-----|
243
+ | Run tests | `npx playwright test` (Bash) | Uses project config, parallel execution, reporters |
244
+ | Write test code | Write/Edit tools | Produces committable, CI-runnable `.spec.ts` files |
245
+ | Generate tests | `npx playwright codegen` (Bash) | Records user actions as code |
246
+ | Debug failures | `npx playwright test --debug` or `--ui` (Bash) | Full trace viewer, time-travel debugging |
247
+ | **Exception**: inspect live DOM | MCP `browser_snapshot` | Only when you need to reason about current page structure interactively |
253
248
 
254
- ### Install CLI
249
+ ### Why CLI Over MCP for Testing
250
+
251
+ 1. **Headless control**: CLI respects `playwright.config.ts` headless settings; MCP always opens a visible browser
252
+ 2. **Config integration**: CLI uses your fixtures, projects, retries, reporters; MCP ignores them all
253
+ 3. **Token efficiency**: CLI returns concise pass/fail output (~250 chars); MCP returns full DOM trees (~5K+ chars per action)
254
+ 4. **Reproducibility**: CLI tests are deterministic `.spec.ts` files; MCP interactions are ephemeral tool calls
255
+ 5. **CI/CD ready**: CLI output works directly in GitHub Actions, Jenkins, etc.
256
+
257
+ ### Install Playwright CLI
255
258
  ```bash
256
- npm install -g @playwright/cli@latest
259
+ npm init playwright@latest
260
+ # or for existing projects:
261
+ npx playwright install
257
262
  ```
258
263
 
259
- ### Configuration
260
- Set preference in `.specweave/config.json`:
261
- ```json
262
- {
263
- "testing": {
264
- "playwright": { "preferCli": true }
265
- }
266
- }
267
- ```
264
+ ### The Only MCP Exception
265
+
266
+ Use MCP Playwright tools **only** for:
267
+ - `/sw-testing:ui-inspect` — interactive element inspection requiring DOM reasoning
268
+ - One-off page exploration when you need to see what's on a page right now
268
269
 
269
- The `sw-testing` skill layer routes automatically based on task type.
270
+ For everything else writing tests, running tests, generating tests, debugging tests — use the CLI via Bash.
270
271
 
271
272
  ## Related Skills
272
273
 
@@ -158,6 +158,8 @@ You are an expert QA engineer with deep knowledge of testing strategies, test au
158
158
 
159
159
  ### 6. End-to-End Testing
160
160
 
161
+ **CLI-First Rule**: Always use Playwright CLI (`npx playwright test`) for E2E test execution. DO NOT use MCP Playwright tools (`browser_click`, `browser_snapshot`, etc.) for running or writing tests — they bypass playwright.config.ts, consume 20x more tokens, and produce non-committable ephemeral interactions instead of reusable test code.
162
+
161
163
  **Playwright Excellence**:
162
164
  - Page Object Model (POM)
163
165
  - Fixtures for setup/teardown