@vizzly-testing/cli 0.10.2 → 0.11.0

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 (48) hide show
  1. package/.claude-plugin/.mcp.json +8 -0
  2. package/.claude-plugin/README.md +114 -0
  3. package/.claude-plugin/commands/debug-diff.md +153 -0
  4. package/.claude-plugin/commands/setup.md +137 -0
  5. package/.claude-plugin/commands/suggest-screenshots.md +111 -0
  6. package/.claude-plugin/commands/tdd-status.md +43 -0
  7. package/.claude-plugin/marketplace.json +28 -0
  8. package/.claude-plugin/mcp/vizzly-server/cloud-api-provider.js +354 -0
  9. package/.claude-plugin/mcp/vizzly-server/index.js +861 -0
  10. package/.claude-plugin/mcp/vizzly-server/local-tdd-provider.js +422 -0
  11. package/.claude-plugin/mcp/vizzly-server/token-resolver.js +185 -0
  12. package/.claude-plugin/plugin.json +14 -0
  13. package/README.md +168 -8
  14. package/dist/cli.js +64 -0
  15. package/dist/client/index.js +13 -3
  16. package/dist/commands/login.js +195 -0
  17. package/dist/commands/logout.js +71 -0
  18. package/dist/commands/project.js +351 -0
  19. package/dist/commands/run.js +30 -0
  20. package/dist/commands/whoami.js +162 -0
  21. package/dist/plugin-loader.js +4 -2
  22. package/dist/sdk/index.js +16 -4
  23. package/dist/services/api-service.js +50 -7
  24. package/dist/services/auth-service.js +226 -0
  25. package/dist/services/tdd-service.js +2 -1
  26. package/dist/types/client/index.d.ts +9 -3
  27. package/dist/types/commands/login.d.ts +11 -0
  28. package/dist/types/commands/logout.d.ts +11 -0
  29. package/dist/types/commands/project.d.ts +28 -0
  30. package/dist/types/commands/whoami.d.ts +11 -0
  31. package/dist/types/sdk/index.d.ts +9 -4
  32. package/dist/types/services/api-service.d.ts +2 -1
  33. package/dist/types/services/auth-service.d.ts +59 -0
  34. package/dist/types/utils/browser.d.ts +6 -0
  35. package/dist/types/utils/config-loader.d.ts +1 -1
  36. package/dist/types/utils/config-schema.d.ts +8 -174
  37. package/dist/types/utils/file-helpers.d.ts +18 -0
  38. package/dist/types/utils/global-config.d.ts +84 -0
  39. package/dist/utils/browser.js +44 -0
  40. package/dist/utils/config-loader.js +69 -3
  41. package/dist/utils/file-helpers.js +64 -0
  42. package/dist/utils/global-config.js +259 -0
  43. package/docs/api-reference.md +177 -6
  44. package/docs/authentication.md +334 -0
  45. package/docs/getting-started.md +21 -2
  46. package/docs/plugins.md +27 -0
  47. package/docs/test-integration.md +60 -10
  48. package/package.json +5 -3
@@ -0,0 +1,334 @@
1
+ # Authentication Guide
2
+
3
+ Vizzly CLI supports flexible authentication to fit different workflows: user authentication for local development and API tokens for CI/CD pipelines.
4
+
5
+ ## Overview
6
+
7
+ The CLI provides two authentication methods:
8
+
9
+ 1. **User Authentication** - OAuth-based login for individual developers
10
+ 2. **API Tokens** - Direct token authentication for automation and CI/CD
11
+
12
+ ## User Authentication (Recommended for Local Development)
13
+
14
+ User authentication uses OAuth 2.0 device flow to securely authenticate with your Vizzly account.
15
+
16
+ ### Login
17
+
18
+ ```bash
19
+ vizzly login
20
+ ```
21
+
22
+ **What happens:**
23
+ 1. CLI displays a device code
24
+ 2. Browser automatically opens to https://app.vizzly.dev/auth/device
25
+ 3. Device code is pre-filled in the form
26
+ 4. You authorize the CLI with your Vizzly account
27
+ 5. Access token is stored securely in `~/.vizzly/config.json`
28
+
29
+ **Features:**
30
+ - 30-day token expiry with automatic refresh
31
+ - Secure storage with 0600 file permissions (Unix/Linux/macOS)
32
+ - Works across all your projects
33
+
34
+ **JSON Output:**
35
+ ```bash
36
+ vizzly login --json
37
+ ```
38
+
39
+ Returns machine-readable output for scripting:
40
+ ```json
41
+ {
42
+ "success": true,
43
+ "user": {
44
+ "email": "you@example.com",
45
+ "name": "Your Name"
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Check Authentication Status
51
+
52
+ ```bash
53
+ vizzly whoami
54
+ ```
55
+
56
+ Shows:
57
+ - Current user email and name
58
+ - Organizations you belong to
59
+ - Token expiry date
60
+ - Project mappings (if configured)
61
+
62
+ **Example output:**
63
+ ```
64
+ Authenticated as you@example.com (Your Name)
65
+
66
+ Organizations:
67
+ - Acme Inc
68
+ - Personal
69
+
70
+ Token expires: 2025-11-15
71
+
72
+ Project mappings:
73
+ /Users/you/projects/acme-app → Acme Inc / Marketing Site
74
+ ```
75
+
76
+ **JSON Output:**
77
+ ```bash
78
+ vizzly whoami --json
79
+ ```
80
+
81
+ ### Logout
82
+
83
+ ```bash
84
+ vizzly logout
85
+ ```
86
+
87
+ Clears all stored authentication:
88
+ - Revokes tokens on the server
89
+ - Removes tokens from `~/.vizzly/config.json`
90
+ - Clears project mappings
91
+
92
+ ## Project-Specific Tokens
93
+
94
+ For multi-project workflows, configure directory-specific tokens.
95
+
96
+ ### Select a Project
97
+
98
+ ```bash
99
+ cd /path/to/project
100
+ vizzly project:select
101
+ ```
102
+
103
+ **Interactive prompts:**
104
+ 1. Choose an organization
105
+ 2. Choose a project
106
+ 3. Token is mapped to current directory path
107
+
108
+ The CLI automatically uses the correct token based on your current directory.
109
+
110
+ ### List Projects
111
+
112
+ ```bash
113
+ vizzly project:list
114
+ ```
115
+
116
+ Shows all configured project mappings:
117
+ ```
118
+ Project mappings:
119
+ /Users/you/projects/acme-app → Acme Inc / Marketing Site
120
+ /Users/you/projects/startup → Personal / Landing Page
121
+ ```
122
+
123
+ ### Show Project Token
124
+
125
+ ```bash
126
+ vizzly project:token
127
+ ```
128
+
129
+ Displays the project token for the current directory (first 10 characters only for security).
130
+
131
+ **JSON Output:**
132
+ ```bash
133
+ vizzly project:token --json
134
+ ```
135
+
136
+ ### Remove Project Configuration
137
+
138
+ ```bash
139
+ vizzly project:remove
140
+ ```
141
+
142
+ Removes the project mapping for the current directory.
143
+
144
+ ## API Token Authentication (Recommended for CI/CD)
145
+
146
+ For automated environments, use project tokens directly.
147
+
148
+ ### Using Environment Variables
149
+
150
+ ```bash
151
+ export VIZZLY_TOKEN=vzt_your_project_token_here
152
+ vizzly run "npm test"
153
+ ```
154
+
155
+ ### Using CLI Flags
156
+
157
+ ```bash
158
+ vizzly run "npm test" --token vzt_your_project_token_here
159
+ ```
160
+
161
+ ### Using .env Files (Local Development)
162
+
163
+ Create a `.env` file in your project root:
164
+
165
+ ```
166
+ VIZZLY_TOKEN=vzt_your_project_token_here
167
+ ```
168
+
169
+ **Important:** Add `.env` to your `.gitignore` to prevent committing secrets.
170
+
171
+ ### CI/CD Integration
172
+
173
+ Use your CI provider's secret management:
174
+
175
+ **GitHub Actions:**
176
+ ```yaml
177
+ - name: Run visual tests
178
+ run: npx vizzly run "npm test" --wait
179
+ env:
180
+ VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
181
+ ```
182
+
183
+ **GitLab CI:**
184
+ ```yaml
185
+ visual-tests:
186
+ script:
187
+ - npx vizzly run "npm test" --wait
188
+ variables:
189
+ VIZZLY_TOKEN: $VIZZLY_TOKEN
190
+ ```
191
+
192
+ **CircleCI:**
193
+ ```yaml
194
+ - run:
195
+ name: Visual tests
196
+ command: npx vizzly run "npm test" --wait
197
+ environment:
198
+ VIZZLY_TOKEN: $VIZZLY_TOKEN
199
+ ```
200
+
201
+ ## Token Resolution Priority
202
+
203
+ The CLI resolves tokens in this order (highest to lowest):
204
+
205
+ 1. **CLI flag** (`--token`)
206
+ 2. **Environment variable** (`VIZZLY_TOKEN`)
207
+ 3. **Project mapping** (from `vizzly project:select`)
208
+ 4. **User access token** (from `vizzly login`)
209
+
210
+ This allows flexible authentication across different contexts.
211
+
212
+ ### Examples
213
+
214
+ **Override with CLI flag:**
215
+ ```bash
216
+ vizzly run "npm test" --token vzt_different_token
217
+ ```
218
+
219
+ **Use project mapping:**
220
+ ```bash
221
+ cd /path/to/project
222
+ vizzly run "npm test" # Uses token from project:select
223
+ ```
224
+
225
+ **Fallback to user token:**
226
+ ```bash
227
+ vizzly login
228
+ cd /new/project
229
+ vizzly run "npm test" # Uses user access token
230
+ ```
231
+
232
+ ## Token Storage
233
+
234
+ Tokens are stored in `~/.vizzly/config.json` with the following structure:
235
+
236
+ ```json
237
+ {
238
+ "accessToken": "vzt_...",
239
+ "refreshToken": "vzr_...",
240
+ "expiresAt": "2025-11-15T10:30:00Z",
241
+ "user": {
242
+ "id": "usr_...",
243
+ "email": "you@example.com",
244
+ "name": "Your Name"
245
+ },
246
+ "projectMappings": {
247
+ "/Users/you/projects/acme-app": {
248
+ "organizationId": "org_...",
249
+ "organizationName": "Acme Inc",
250
+ "projectId": "prj_...",
251
+ "projectName": "Marketing Site",
252
+ "token": "vzt_..."
253
+ }
254
+ }
255
+ }
256
+ ```
257
+
258
+ **Security:**
259
+ - File created with `0600` permissions (owner read/write only)
260
+ - On Windows, permissions are set via ACLs when possible
261
+ - Never commit this file to version control
262
+
263
+ ## Token Refresh
264
+
265
+ Access tokens expire after 30 days. The CLI automatically refreshes tokens:
266
+
267
+ - **5-minute expiry buffer** - Tokens are refreshed 5 minutes before expiry
268
+ - **Automatic refresh on 401** - If a request fails with 401, token is refreshed and retried
269
+ - **Refresh tokens** - Long-lived refresh tokens are used to obtain new access tokens
270
+
271
+ You don't need to manually manage token refresh.
272
+
273
+ ## Troubleshooting
274
+
275
+ ### "Not authenticated" error
276
+
277
+ **Solution:**
278
+ ```bash
279
+ vizzly login
280
+ ```
281
+
282
+ Or set `VIZZLY_TOKEN` environment variable.
283
+
284
+ ### "Token expired" error
285
+
286
+ **Solution:**
287
+ The CLI should auto-refresh. If it doesn't:
288
+ ```bash
289
+ vizzly logout
290
+ vizzly login
291
+ ```
292
+
293
+ ### Permission denied writing config file
294
+
295
+ **Linux/macOS:**
296
+ ```bash
297
+ chmod 700 ~/.vizzly
298
+ chmod 600 ~/.vizzly/config.json
299
+ ```
300
+
301
+ **Windows:**
302
+ Run CLI as administrator or check file permissions.
303
+
304
+ ### Project token not being used
305
+
306
+ **Solution:**
307
+ Verify project mapping:
308
+ ```bash
309
+ vizzly project:list
310
+ ```
311
+
312
+ Check you're in the correct directory. The CLI traverses parent directories to find project mappings.
313
+
314
+ ### Browser doesn't open during login
315
+
316
+ **Solution:**
317
+ 1. Copy the device code shown in the terminal
318
+ 2. Manually visit https://app.vizzly.dev/auth/device
319
+ 3. Paste the device code
320
+ 4. Press Enter in the terminal after authorizing
321
+
322
+ ## Security Best Practices
323
+
324
+ 1. **Never commit tokens** - Add `.env` and `~/.vizzly/config.json` to `.gitignore`
325
+ 2. **Use CI secrets** - Store `VIZZLY_TOKEN` in your CI provider's secret manager
326
+ 3. **Rotate tokens regularly** - Generate new project tokens periodically
327
+ 4. **Use project tokens in CI** - Don't use personal access tokens in shared pipelines
328
+ 5. **Limit token scope** - Use project-specific tokens instead of organization-wide tokens when possible
329
+
330
+ ## Next Steps
331
+
332
+ - [Getting Started Guide](./getting-started.md)
333
+ - [Test Integration Guide](./test-integration.md)
334
+ - [API Reference](./api-reference.md)
@@ -22,12 +22,31 @@ npx vizzly init
22
22
 
23
23
  This creates a basic `vizzly.config.js` file with sensible defaults.
24
24
 
25
- ### 2. Set up your API token
25
+ ### 2. Authenticate
26
+
27
+ **Option 1: User Authentication (Recommended for local development)**
26
28
 
27
29
  ```bash
28
- export VIZZLY_TOKEN=your-api-token
30
+ # Authenticate with your Vizzly account
31
+ npx vizzly login
32
+
33
+ # Optional: Configure project-specific token for this directory
34
+ npx vizzly project:select
29
35
  ```
30
36
 
37
+ **Option 2: API Token (Recommended for CI/CD)**
38
+
39
+ ```bash
40
+ export VIZZLY_TOKEN=your-project-token
41
+ ```
42
+
43
+ **Token Priority:**
44
+ The CLI resolves tokens in this order:
45
+ 1. CLI flag (`--token`)
46
+ 2. Environment variable (`VIZZLY_TOKEN`)
47
+ 3. Project mapping (from `vizzly project:select`)
48
+ 4. User access token (from `vizzly login`)
49
+
31
50
  ### 3. Verify your setup
32
51
 
33
52
  Run a fast local preflight:
package/docs/plugins.md CHANGED
@@ -22,6 +22,33 @@ file.
22
22
 
23
23
  ### Official Plugins
24
24
 
25
+ #### Claude Code Integration (Built-in)
26
+
27
+ Vizzly includes a built-in plugin for [Claude Code](https://claude.com/code), providing AI-powered visual testing workflows:
28
+
29
+ **Installation via Claude Code:**
30
+ ```
31
+ /plugin marketplace add vizzly-testing/cli
32
+ ```
33
+
34
+ **Available Commands:**
35
+ - `/vizzly:tdd-status` - Check TDD dashboard status with AI insights
36
+ - `/vizzly:debug-diff <screenshot-name>` - Analyze visual failures with AI assistance
37
+ - `/vizzly:suggest-screenshots` - Get framework-specific test coverage suggestions
38
+ - `/vizzly:setup` - Interactive setup wizard for Vizzly configuration
39
+
40
+ The plugin automatically detects whether you're in local TDD mode or working with cloud builds, providing contextual help for your workflow.
41
+
42
+ **Features:**
43
+ - MCP (Model Context Protocol) server for tool integration
44
+ - 15+ specialized tools for local TDD and cloud API workflows
45
+ - Intelligent context detection and mode switching
46
+ - Image analysis without API errors (safe baseline/current image handling)
47
+
48
+ **Location:** `.claude-plugin/` directory in the repository
49
+
50
+ #### Other Official Plugins
51
+
25
52
  Official Vizzly plugins are published under the `@vizzly-testing/*` scope and are automatically
26
53
  discovered:
27
54
 
@@ -64,7 +64,9 @@ The CLI automatically sets these variables for your test process:
64
64
 
65
65
  ## Adding Screenshots to Tests
66
66
 
67
- Import the client and use `vizzlyScreenshot()` in your tests:
67
+ Import the client and use `vizzlyScreenshot()` in your tests. You can pass either a **Buffer** or a **file path**:
68
+
69
+ ### Using a Buffer
68
70
 
69
71
  ```javascript
70
72
  import { vizzlyScreenshot } from '@vizzly-testing/cli/client';
@@ -81,6 +83,28 @@ await vizzlyScreenshot('homepage', screenshot, {
81
83
  });
82
84
  ```
83
85
 
86
+ ### Using a File Path
87
+
88
+ ```javascript
89
+ import { vizzlyScreenshot } from '@vizzly-testing/cli/client';
90
+
91
+ // Save screenshot to file first
92
+ await page.screenshot({ path: './screenshots/homepage.png' });
93
+
94
+ // Send to Vizzly using file path
95
+ await vizzlyScreenshot('homepage', './screenshots/homepage.png', {
96
+ properties: {
97
+ browser: 'chrome',
98
+ viewport: '1920x1080'
99
+ }
100
+ });
101
+ ```
102
+
103
+ **File path support:**
104
+ - Accepts both absolute and relative paths
105
+ - Works with any tool that generates PNG files
106
+ - Useful when screenshots are already saved to disk
107
+
84
108
  ## Framework Examples
85
109
 
86
110
  ### Playwright
@@ -91,7 +115,8 @@ import { vizzlyScreenshot } from '@vizzly-testing/cli/client';
91
115
 
92
116
  test('homepage test', async ({ page }) => {
93
117
  await page.goto('/');
94
-
118
+
119
+ // Using a Buffer
95
120
  const screenshot = await page.screenshot();
96
121
  await vizzlyScreenshot('homepage', screenshot, {
97
122
  properties: {
@@ -100,6 +125,16 @@ test('homepage test', async ({ page }) => {
100
125
  page: 'home'
101
126
  }
102
127
  });
128
+
129
+ // Using a file path
130
+ await page.screenshot({ path: './screenshots/homepage.png' });
131
+ await vizzlyScreenshot('homepage', './screenshots/homepage.png', {
132
+ properties: {
133
+ browser: 'chrome',
134
+ viewport: '1920x1080',
135
+ page: 'home'
136
+ }
137
+ });
103
138
  });
104
139
  ```
105
140
 
@@ -108,20 +143,25 @@ test('homepage test', async ({ page }) => {
108
143
  ```javascript
109
144
  // cypress/support/commands.js
110
145
  import { vizzlyScreenshot } from '@vizzly-testing/cli/client';
146
+ import { join } from 'path';
111
147
 
148
+ // Using file paths
112
149
  Cypress.Commands.add('vizzlyScreenshot', (name, properties = {}) => {
150
+ // Cypress saves screenshots to cypress/screenshots by default
113
151
  cy.screenshot(name, { capture: 'viewport' });
114
-
115
- cy.readFile(`cypress/screenshots/${name}.png`, 'base64').then((imageBase64) => {
116
- const imageBuffer = Buffer.from(imageBase64, 'base64');
117
- return vizzlyScreenshot(name, imageBuffer, {
152
+
153
+ // Use file path directly
154
+ const screenshotPath = join(Cypress.config('screenshotsFolder'), `${name}.png`);
155
+
156
+ return cy.wrap(
157
+ vizzlyScreenshot(name, screenshotPath, {
118
158
  properties: {
119
159
  browser: Cypress.browser.name,
120
160
  framework: 'cypress',
121
161
  ...properties
122
162
  }
123
- });
124
- });
163
+ })
164
+ );
125
165
  });
126
166
 
127
167
  // In your test
@@ -147,7 +187,8 @@ describe('Visual tests', () => {
147
187
  const browser = await puppeteer.launch();
148
188
  const page = await browser.newPage();
149
189
  await page.goto('/');
150
-
190
+
191
+ // Using a Buffer
151
192
  const screenshot = await page.screenshot();
152
193
  await vizzlyScreenshot('homepage', screenshot, {
153
194
  properties: {
@@ -155,7 +196,16 @@ describe('Visual tests', () => {
155
196
  framework: 'puppeteer'
156
197
  }
157
198
  });
158
-
199
+
200
+ // Using a file path
201
+ await page.screenshot({ path: './screenshots/homepage.png' });
202
+ await vizzlyScreenshot('homepage', './screenshots/homepage.png', {
203
+ properties: {
204
+ browser: 'chrome',
205
+ framework: 'puppeteer'
206
+ }
207
+ });
208
+
159
209
  await browser.close();
160
210
  });
161
211
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizzly-testing/cli",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "Visual review platform for UI developers and designers",
5
5
  "keywords": [
6
6
  "visual-testing",
@@ -48,6 +48,7 @@
48
48
  "bin",
49
49
  "dist",
50
50
  "docs",
51
+ ".claude-plugin",
51
52
  "README.md",
52
53
  "LICENSE"
53
54
  ],
@@ -84,7 +85,7 @@
84
85
  "form-data": "^4.0.0",
85
86
  "glob": "^11.0.3",
86
87
  "odiff-bin": "^3.2.1",
87
- "zod": "^3.25.76"
88
+ "zod": "^4.1.12"
88
89
  },
89
90
  "devDependencies": {
90
91
  "@babel/cli": "^7.28.0",
@@ -94,6 +95,7 @@
94
95
  "@babel/preset-typescript": "^7.23.6",
95
96
  "@eslint/js": "^9.31.0",
96
97
  "@heroicons/react": "^2.2.0",
98
+ "@modelcontextprotocol/sdk": "^1.0.4",
97
99
  "@playwright/test": "^1.55.1",
98
100
  "@tailwindcss/postcss": "^4.1.13",
99
101
  "@vitejs/plugin-react": "^5.0.3",
@@ -104,7 +106,7 @@
104
106
  "eslint-config-prettier": "^10.1.8",
105
107
  "eslint-plugin-prettier": "^5.5.3",
106
108
  "eslint-plugin-react": "^7.37.5",
107
- "eslint-plugin-react-hooks": "^6.1.1",
109
+ "eslint-plugin-react-hooks": "^7.0.0",
108
110
  "postcss": "^8.5.6",
109
111
  "prettier": "^3.6.2",
110
112
  "react": "^19.1.1",