whisker-ux 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Eric Li
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # Whisker
2
+
3
+ AI-powered usability testing CLI. Whisker uses Claude's computer use capability to navigate your website like a real user, identifying UX issues, bugs, and accessibility problems.
4
+
5
+ ```
6
+ ╭────────────────────────────────────────────────────────────────────╮
7
+ │ │
8
+ │ WHISKER v0.1.0 │
9
+ │ AI-Powered Usability Testing │
10
+ │ │
11
+ │ _._ _,-'""`-._ │
12
+ │ (,-.`._,'( |`-/| │
13
+ │ `-.-' \ )-`( , o o) │
14
+ │ `- \`_`"'- │
15
+ │ │
16
+ ╰────────────────────────────────────────────────────────────────────╯
17
+ ```
18
+
19
+ ## Features
20
+
21
+ - **AI-Powered Navigation**: Claude navigates your site like a real user, completing tasks and noting issues
22
+ - **Think-Aloud Testing**: Get insights into user confusion and friction points
23
+ - **Comprehensive Reports**: Detailed findings with severity levels, reproduction steps, and suggested fixes
24
+ - **Screenshot Documentation**: Every step is captured for easy reference
25
+ - **Bug Detection**: Catches console errors, network failures, and visual issues
26
+ - **Developer-Friendly Output**: Includes grep patterns to find relevant code
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install -g whisker-ux
32
+ ```
33
+
34
+ Or run directly with npx:
35
+
36
+ ```bash
37
+ npx whisker-ux "Sign up for an account" --url https://your-site.com
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ 1. **Set up your Anthropic API key**:
43
+
44
+ ```bash
45
+ whisker setup
46
+ ```
47
+
48
+ Or set the environment variable:
49
+
50
+ ```bash
51
+ export ANTHROPIC_API_KEY=sk-ant-...
52
+ ```
53
+
54
+ 2. **Run a usability test**:
55
+
56
+ ```bash
57
+ whisker "Add a product to the shopping cart" --url http://localhost:3000
58
+ ```
59
+
60
+ 3. **View the results** in `.whisker/report.md`
61
+
62
+ ## Usage
63
+
64
+ ```bash
65
+ whisker <task> --url <url> [options]
66
+ ```
67
+
68
+ ### Arguments
69
+
70
+ - `<task>` - The task to complete (e.g., "Sign up for an account", "Find the pricing page")
71
+
72
+ ### Options
73
+
74
+ | Option | Description | Default |
75
+ |--------|-------------|---------|
76
+ | `-u, --url <url>` | URL of the site to test (required) | - |
77
+ | `-p, --persona <persona>` | Persona description for the tester | - |
78
+ | `-m, --max-steps <number>` | Maximum navigation steps | 50 |
79
+ | `-v, --viewport <WxH>` | Viewport size | 1280x800 |
80
+ | `-o, --output <dir>` | Output directory | .whisker |
81
+
82
+ ### Examples
83
+
84
+ **Basic test:**
85
+ ```bash
86
+ whisker "Find the contact page" --url https://example.com
87
+ ```
88
+
89
+ **With persona:**
90
+ ```bash
91
+ whisker "Complete the checkout flow" \
92
+ --url http://localhost:3000 \
93
+ --persona "First-time user, not tech-savvy, age 65"
94
+ ```
95
+
96
+ **Custom viewport (mobile):**
97
+ ```bash
98
+ whisker "Navigate to settings" \
99
+ --url https://myapp.com \
100
+ --viewport 375x667
101
+ ```
102
+
103
+ **Limit steps:**
104
+ ```bash
105
+ whisker "Find pricing information" \
106
+ --url https://startup.com \
107
+ --max-steps 10
108
+ ```
109
+
110
+ ## Output
111
+
112
+ Whisker generates three outputs in the `.whisker` directory:
113
+
114
+ ```
115
+ .whisker/
116
+ ├── report.md # Human-readable findings report
117
+ ├── report.json # Structured JSON for automation
118
+ └── screenshots/
119
+ ├── step-001.png
120
+ ├── step-002.png
121
+ └── ...
122
+ ```
123
+
124
+ ### Report Structure
125
+
126
+ The report includes:
127
+
128
+ - **Task completion status** - Whether the AI successfully completed the task
129
+ - **Summary** - Overview of the testing session
130
+ - **Findings** - Detailed issues organized by priority:
131
+ - 🔴 P0 - Critical, blocking issues
132
+ - 🟠 P1 - Major problems
133
+ - 🟡 P2 - Minor issues
134
+ - 💡 P3 - Suggestions
135
+ - **Console errors** - JavaScript errors detected
136
+ - **Network failures** - Failed API calls and requests
137
+
138
+ ### Finding Details
139
+
140
+ Each finding includes:
141
+
142
+ - Severity and category (bug, ux-friction, accessibility, performance, visual, copy)
143
+ - Description with expected vs actual behavior
144
+ - Steps to reproduce
145
+ - Screenshot reference
146
+ - Suggested fix
147
+ - Grep patterns to find relevant code
148
+
149
+ ## Commands
150
+
151
+ ### `whisker setup`
152
+
153
+ Configure your Anthropic API key interactively.
154
+
155
+ ### `whisker logout`
156
+
157
+ Remove your stored API key.
158
+
159
+ ### `whisker run <task>` (default)
160
+
161
+ Run a usability test. This is the default command.
162
+
163
+ ## Configuration
164
+
165
+ ### API Key
166
+
167
+ Whisker looks for your Anthropic API key in this order:
168
+
169
+ 1. `ANTHROPIC_API_KEY` environment variable
170
+ 2. `~/.whisker/config.json` (set via `whisker setup`)
171
+
172
+ ### Requirements
173
+
174
+ - Node.js 18+
175
+ - Anthropic API key with access to Claude's computer use capability
176
+
177
+ ## Programmatic Usage
178
+
179
+ ```typescript
180
+ import { runSession, writeReport, WhiskerConfig } from 'whisker-ux';
181
+
182
+ const config: WhiskerConfig = {
183
+ task: "Sign up for an account",
184
+ url: "https://example.com",
185
+ maxSteps: 50,
186
+ viewport: { width: 1280, height: 800 },
187
+ outputDir: ".whisker"
188
+ };
189
+
190
+ const { report, sessionLog } = await runSession(config);
191
+ await writeReport(report, sessionLog, config.outputDir);
192
+
193
+ console.log(`Task completed: ${report.taskCompleted}`);
194
+ console.log(`Found ${report.findings.length} issues`);
195
+ ```
196
+
197
+ ## Troubleshooting
198
+
199
+ ### "No Anthropic API key configured"
200
+
201
+ Run `whisker setup` or set the `ANTHROPIC_API_KEY` environment variable.
202
+
203
+ ### Playwright browser issues
204
+
205
+ Whisker uses Playwright's Chromium. If you encounter browser issues:
206
+
207
+ ```bash
208
+ npx playwright install chromium
209
+ ```
210
+
211
+ ### Rate limiting
212
+
213
+ If you hit API rate limits, reduce `--max-steps` or wait before retrying.
214
+
215
+ ### Browser not visible
216
+
217
+ The browser window should appear during testing. If it doesn't, ensure you're not running in a headless environment.
218
+
219
+ ## How It Works
220
+
221
+ 1. **Launch**: Opens a visible Chromium browser at your URL
222
+ 2. **Navigate**: Claude sees the screen and decides what actions to take (click, type, scroll)
223
+ 3. **Think Aloud**: Claude narrates its thought process, noting confusion or issues
224
+ 4. **Capture**: Screenshots are taken after each action
225
+ 5. **Analyze**: Claude reviews the session observations and screenshots to identify issues that may not be obvious during navigation
226
+ 6. **Report**: Results are saved as markdown and JSON
227
+
228
+ ## License
229
+
230
+ MIT
231
+
232
+ ## Contributing
233
+
234
+ Issues and pull requests welcome at [github.com/ericli/whisker-ux](https://github.com/ericli/whisker-ux).
@@ -0,0 +1,5 @@
1
+ import { WhiskerConfig, SessionLog, WhiskerReport } from "./types.js";
2
+ export declare function runSession(config: WhiskerConfig): Promise<{
3
+ report: WhiskerReport;
4
+ sessionLog: SessionLog;
5
+ }>;
package/dist/agent.js ADDED
@@ -0,0 +1,324 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ import { BrowserManager } from "./browser.js";
3
+ import { getNavigationSystemPrompt, getReportSystemPrompt, } from "./prompts.js";
4
+ import { printTestStart, startSpinner, stopSpinner, startWaitingSpinner, clearSpinner, printStepComplete } from "./ui.js";
5
+ const MODEL = "claude-sonnet-4-5-20250929";
6
+ const MAX_SCREENSHOTS_FOR_ANALYSIS = 10; // Limit screenshots sent to analysis phase
7
+ const BETA_FLAG = "computer-use-2025-01-24";
8
+ export async function runSession(config) {
9
+ const client = new Anthropic();
10
+ const browser = new BrowserManager(config);
11
+ printTestStart();
12
+ try {
13
+ startSpinner("Launching browser...");
14
+ await browser.launch();
15
+ stopSpinner(`Browser ready at ${config.url}`);
16
+ const sessionLog = await navigationPhase(client, browser, config);
17
+ printStepComplete(`Navigation complete (${sessionLog.steps.length} steps)`);
18
+ startSpinner("Generating report...");
19
+ const report = await reportPhase(client, sessionLog);
20
+ stopSpinner("Report generated");
21
+ return { report, sessionLog };
22
+ }
23
+ finally {
24
+ await browser.close();
25
+ }
26
+ }
27
+ async function navigationPhase(client, browser, config) {
28
+ const startTime = Date.now();
29
+ const steps = [];
30
+ const observations = [];
31
+ const computerTool = {
32
+ type: "computer_20250124",
33
+ name: "computer",
34
+ display_width_px: config.viewport.width,
35
+ display_height_px: config.viewport.height,
36
+ display_number: 1,
37
+ };
38
+ const systemPrompt = getNavigationSystemPrompt(config);
39
+ const messages = [
40
+ {
41
+ role: "user",
42
+ content: `Please begin the usability test. The browser is already open to ${config.url}. Complete this task: ${config.task}`,
43
+ },
44
+ ];
45
+ let stepCount = 0;
46
+ while (stepCount < config.maxSteps) {
47
+ // Show waiting spinner while waiting for API response
48
+ startWaitingSpinner();
49
+ let response;
50
+ try {
51
+ response = await client.beta.messages.create({
52
+ model: MODEL,
53
+ max_tokens: 4096,
54
+ system: systemPrompt,
55
+ tools: [computerTool],
56
+ messages,
57
+ betas: [BETA_FLAG],
58
+ });
59
+ }
60
+ catch (err) {
61
+ clearSpinner();
62
+ stopSpinner(`API error: ${err}`);
63
+ throw err;
64
+ }
65
+ // Clear waiting spinner when we get a response
66
+ clearSpinner();
67
+ // Append assistant response to messages
68
+ messages.push({
69
+ role: "assistant",
70
+ content: response.content,
71
+ });
72
+ // Process content blocks
73
+ const toolResults = [];
74
+ let hasToolUse = false;
75
+ let currentThinking = "";
76
+ // First pass: collect thinking text
77
+ for (const block of response.content) {
78
+ if (block.type === "text") {
79
+ observations.push(block.text);
80
+ currentThinking += block.text + " ";
81
+ }
82
+ }
83
+ // Second pass: process tool uses with the collected thinking
84
+ for (const block of response.content) {
85
+ if (block.type === "tool_use") {
86
+ hasToolUse = true;
87
+ stepCount++;
88
+ const action = block.input;
89
+ const actionDesc = formatAction(action);
90
+ // Show spinner while executing
91
+ startSpinner(`Step ${stepCount}: ${actionDesc}`);
92
+ // Execute the action
93
+ if (action.action !== "screenshot") {
94
+ try {
95
+ await browser.executeAction(action);
96
+ }
97
+ catch (err) {
98
+ // Print failed step
99
+ stopSpinner(`Step ${stepCount}: ${actionDesc} (failed)`);
100
+ toolResults.push({
101
+ type: "tool_result",
102
+ tool_use_id: block.id,
103
+ content: `Error executing action: ${err instanceof Error ? err.message : String(err)}`,
104
+ is_error: true,
105
+ });
106
+ continue;
107
+ }
108
+ }
109
+ // Take screenshot after action
110
+ const screenshotBase64 = await browser.takeScreenshot();
111
+ // Print completed step
112
+ stopSpinner(`Step ${stepCount}: ${actionDesc}`);
113
+ steps.push({
114
+ stepNumber: stepCount,
115
+ action,
116
+ screenshotBase64,
117
+ timestamp: Date.now(),
118
+ });
119
+ toolResults.push({
120
+ type: "tool_result",
121
+ tool_use_id: block.id,
122
+ content: [
123
+ {
124
+ type: "image",
125
+ source: {
126
+ type: "base64",
127
+ media_type: "image/png",
128
+ data: screenshotBase64,
129
+ },
130
+ },
131
+ ],
132
+ });
133
+ }
134
+ }
135
+ // If no tool use, Claude is done
136
+ if (!hasToolUse) {
137
+ break;
138
+ }
139
+ // Send tool results back
140
+ messages.push({
141
+ role: "user",
142
+ content: toolResults,
143
+ });
144
+ }
145
+ if (stepCount >= config.maxSteps) {
146
+ printStepComplete(`Reached max step limit (${config.maxSteps})`);
147
+ }
148
+ return {
149
+ config,
150
+ steps,
151
+ observations,
152
+ consoleErrors: browser.getConsoleErrors(),
153
+ networkFailures: browser.getNetworkFailures(),
154
+ startTime,
155
+ endTime: Date.now(),
156
+ };
157
+ }
158
+ function formatAction(action) {
159
+ switch (action.action) {
160
+ case "screenshot":
161
+ return "screenshot";
162
+ case "left_click":
163
+ return `click at (${action.coordinate?.[0]}, ${action.coordinate?.[1]})`;
164
+ case "right_click":
165
+ return `right-click at (${action.coordinate?.[0]}, ${action.coordinate?.[1]})`;
166
+ case "double_click":
167
+ return `double-click at (${action.coordinate?.[0]}, ${action.coordinate?.[1]})`;
168
+ case "type":
169
+ return `type "${action.text?.slice(0, 30)}${(action.text?.length ?? 0) > 30 ? "..." : ""}"`;
170
+ case "key":
171
+ return `key "${action.text}"`;
172
+ case "scroll":
173
+ return `scroll ${action.scroll_direction} at (${action.coordinate?.[0]}, ${action.coordinate?.[1]})`;
174
+ case "mouse_move":
175
+ return `move to (${action.coordinate?.[0]}, ${action.coordinate?.[1]})`;
176
+ default:
177
+ return action.action;
178
+ }
179
+ }
180
+ async function reportPhase(client, sessionLog) {
181
+ const systemPrompt = getReportSystemPrompt();
182
+ const sessionSummary = buildSessionSummary(sessionLog);
183
+ // Build content with text summary and screenshots
184
+ const content = [];
185
+ // Add text summary first
186
+ content.push({
187
+ type: "text",
188
+ text: sessionSummary,
189
+ });
190
+ // Add screenshots (sample if too many)
191
+ const steps = sessionLog.steps;
192
+ let screenshotsToInclude;
193
+ if (steps.length <= MAX_SCREENSHOTS_FOR_ANALYSIS) {
194
+ screenshotsToInclude = steps;
195
+ }
196
+ else {
197
+ // Sample evenly: always include first and last, plus evenly spaced middle ones
198
+ screenshotsToInclude = [steps[0]];
199
+ const middleCount = MAX_SCREENSHOTS_FOR_ANALYSIS - 2;
200
+ const interval = (steps.length - 2) / (middleCount + 1);
201
+ for (let i = 1; i <= middleCount; i++) {
202
+ const idx = Math.round(i * interval);
203
+ if (idx > 0 && idx < steps.length - 1) {
204
+ screenshotsToInclude.push(steps[idx]);
205
+ }
206
+ }
207
+ screenshotsToInclude.push(steps[steps.length - 1]);
208
+ }
209
+ // Add each screenshot with label
210
+ for (const step of screenshotsToInclude) {
211
+ content.push({
212
+ type: "text",
213
+ text: `\n[Screenshot from Step ${step.stepNumber}: ${formatAction(step.action)}]`,
214
+ });
215
+ content.push({
216
+ type: "image",
217
+ source: {
218
+ type: "base64",
219
+ media_type: "image/png",
220
+ data: step.screenshotBase64,
221
+ },
222
+ });
223
+ }
224
+ content.push({
225
+ type: "text",
226
+ text: "\n\nPlease analyze the session log and screenshots above, then produce a structured JSON report of findings.",
227
+ });
228
+ const response = await client.messages.create({
229
+ model: MODEL,
230
+ max_tokens: 4096,
231
+ system: systemPrompt,
232
+ messages: [
233
+ {
234
+ role: "user",
235
+ content,
236
+ },
237
+ ],
238
+ });
239
+ const textBlock = response.content.find((b) => b.type === "text");
240
+ if (!textBlock || textBlock.type !== "text") {
241
+ throw new Error("No text response from Claude in report phase");
242
+ }
243
+ // Parse JSON - strip markdown fences if present
244
+ let jsonStr = textBlock.text.trim();
245
+ if (jsonStr.startsWith("```json")) {
246
+ jsonStr = jsonStr.slice(7);
247
+ }
248
+ else if (jsonStr.startsWith("```")) {
249
+ jsonStr = jsonStr.slice(3);
250
+ }
251
+ if (jsonStr.endsWith("```")) {
252
+ jsonStr = jsonStr.slice(0, -3);
253
+ }
254
+ jsonStr = jsonStr.trim();
255
+ let parsed;
256
+ try {
257
+ parsed = JSON.parse(jsonStr);
258
+ }
259
+ catch (err) {
260
+ throw new Error(`Failed to parse report JSON: ${err instanceof Error ? err.message : String(err)}`);
261
+ }
262
+ return {
263
+ metadata: {
264
+ task: sessionLog.config.task,
265
+ url: sessionLog.config.url,
266
+ persona: sessionLog.config.persona,
267
+ timestamp: new Date(sessionLog.startTime).toISOString(),
268
+ duration: sessionLog.endTime - sessionLog.startTime,
269
+ totalSteps: sessionLog.steps.length,
270
+ modelUsed: MODEL,
271
+ },
272
+ summary: parsed.summary,
273
+ findings: parsed.findings || [],
274
+ consoleErrors: sessionLog.consoleErrors,
275
+ networkFailures: sessionLog.networkFailures,
276
+ taskCompleted: parsed.taskCompleted,
277
+ taskCompletionNotes: parsed.taskCompletionNotes,
278
+ };
279
+ }
280
+ function buildSessionSummary(sessionLog) {
281
+ const parts = [];
282
+ parts.push("# Usability Test Session Log");
283
+ parts.push("");
284
+ parts.push("## Test Configuration");
285
+ parts.push(`- **Task:** ${sessionLog.config.task}`);
286
+ parts.push(`- **URL:** ${sessionLog.config.url}`);
287
+ if (sessionLog.config.persona) {
288
+ parts.push(`- **Persona:** ${sessionLog.config.persona}`);
289
+ }
290
+ parts.push(`- **Duration:** ${((sessionLog.endTime - sessionLog.startTime) / 1000).toFixed(1)}s`);
291
+ parts.push(`- **Total Steps:** ${sessionLog.steps.length}`);
292
+ parts.push("");
293
+ parts.push("## Tester Observations");
294
+ parts.push("(These are the tester's think-aloud comments during navigation)");
295
+ parts.push("");
296
+ for (const obs of sessionLog.observations) {
297
+ parts.push(obs);
298
+ parts.push("");
299
+ }
300
+ parts.push("## Actions Taken");
301
+ for (const step of sessionLog.steps) {
302
+ const actionDesc = formatAction(step.action);
303
+ parts.push(`${step.stepNumber}. ${actionDesc}`);
304
+ }
305
+ parts.push("");
306
+ if (sessionLog.consoleErrors.length > 0) {
307
+ parts.push("## Console Errors Detected");
308
+ for (const err of sessionLog.consoleErrors) {
309
+ parts.push(`- ${err}`);
310
+ }
311
+ parts.push("");
312
+ }
313
+ if (sessionLog.networkFailures.length > 0) {
314
+ parts.push("## Network Failures Detected");
315
+ for (const fail of sessionLog.networkFailures) {
316
+ parts.push(`- ${fail.method} ${fail.url} → ${fail.status} ${fail.statusText}`);
317
+ }
318
+ parts.push("");
319
+ }
320
+ parts.push("---");
321
+ parts.push("Please analyze this session and produce a structured JSON report of findings.");
322
+ return parts.join("\n");
323
+ }
324
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { ComputerAction, NetworkFailure, WhiskerConfig } from "./types.js";
2
+ export declare class BrowserManager {
3
+ private config;
4
+ private browser;
5
+ private context;
6
+ private page;
7
+ private consoleErrors;
8
+ private networkFailures;
9
+ constructor(config: WhiskerConfig);
10
+ launch(): Promise<void>;
11
+ takeScreenshot(): Promise<string>;
12
+ executeAction(action: ComputerAction): Promise<void>;
13
+ close(): Promise<void>;
14
+ getConsoleErrors(): string[];
15
+ getNetworkFailures(): NetworkFailure[];
16
+ }