@haystackeditor/cli 0.10.1 → 0.10.3

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.
@@ -1,639 +0,0 @@
1
- # Haystack Verification Setup
2
-
3
- > **PRO Feature**: Verification Flow requires a Haystack PRO subscription to run cloud sandboxes. Free users can still set up the `.haystack.json` configuration — it will be ready when they upgrade. See https://haystackeditor.com/pricing for details.
4
-
5
- **Your job**: Help the verification system understand this app so it can visually verify PRs work correctly.
6
-
7
- The verification system has two parts:
8
- 1. **Planner** - An AI that explores the codebase to figure out what to test
9
- 2. **Executor** - Takes screenshots based on the plan
10
-
11
- Your `.haystack.json` flows feed into the **corpus** - they're hints for the Planner about what routes exist, what selectors to use, and what the core user journey looks like.
12
-
13
- ---
14
-
15
- ## Step 0: Configure Dev Server for Cloud Sandboxes
16
-
17
- Cloud sandboxes access your dev server via tunnel URLs (`*.modal.host`). Most modern dev servers block non-localhost hosts by default. **You must configure your dev server to allow external hosts.**
18
-
19
- ### Vite / SvelteKit / Astro / Nuxt 3
20
-
21
- ```typescript
22
- // vite.config.ts (or framework equivalent)
23
- export default defineConfig({
24
- server: {
25
- allowedHosts: true, // Required for Haystack cloud sandboxes
26
- },
27
- });
28
- ```
29
-
30
- ### Next.js
31
-
32
- Next.js allows all hosts by default in development. No configuration needed.
33
-
34
- If you've restricted hosts, add to `next.config.js`:
35
- ```javascript
36
- module.exports = {
37
- allowedDevHosts: ['localhost', '.modal.host'],
38
- };
39
- ```
40
-
41
- ### Webpack Dev Server (CRA, Vue CLI, custom)
42
-
43
- ```javascript
44
- // webpack.config.js or vue.config.js
45
- module.exports = {
46
- devServer: {
47
- allowedHosts: 'all', // or ['.modal.host']
48
- },
49
- };
50
- ```
51
-
52
- For Create React App, set environment variable:
53
- ```bash
54
- DANGEROUSLY_DISABLE_HOST_CHECK=true npm start
55
- ```
56
-
57
- ### Angular
58
-
59
- ```json
60
- // angular.json → serve → options
61
- {
62
- "disableHostCheck": true
63
- }
64
- ```
65
-
66
- Or run with: `ng serve --disable-host-check`
67
-
68
- ### Remix
69
-
70
- Remix dev server allows all hosts by default. No configuration needed.
71
-
72
- ### Other Frameworks
73
-
74
- Look for `allowedHosts`, `disableHostCheck`, or `host` options in your dev server config. The goal is to allow requests from `*.modal.host` domains.
75
-
76
- **Skip this step if**: Your dev server already allows external hosts or doesn't have host checking.
77
-
78
- ---
79
-
80
- ## Step 1: Generate Base Config
81
-
82
- ```bash
83
- npx @haystackeditor/cli init --yes
84
- ```
85
-
86
- This detects your dev server command, port, and service dependencies.
87
-
88
- ---
89
-
90
- ## Step 2: Understand the App's Core Feature
91
-
92
- Before writing any flows, answer this:
93
-
94
- **What is the ONE main thing users do in this app?**
95
-
96
- Read the codebase to understand the core user journey:
97
- - E-commerce: browse products → add to cart → checkout
98
- - Dashboard: view metrics → filter data → export
99
- - Editor: create document → edit → save
100
- - Social: view feed → create post → interact
101
- - SaaS: sign up → configure → use feature
102
-
103
- Your flows should describe THIS journey, not just "pages load".
104
-
105
- ---
106
-
107
- ## Step 3: Verify ALL Services Have Flows (CRITICAL)
108
-
109
- **Every service in `.haystack.json` must have at least one verification flow.** This is the most common mistake - adding services but forgetting to add flows that exercise them.
110
-
111
- ### Check for Uncovered Services
112
-
113
- ```bash
114
- # List all services
115
- grep -A1 '"services"' .haystack.json | grep -E '^\s+"[^"]+":' | sed 's/.*"\([^"]*\)".*/\1/'
116
-
117
- # List all flows
118
- grep '"name":' .haystack.json
119
-
120
- # MANUAL CHECK: Does every service have a flow?
121
- ```
122
-
123
- ### Types of Services
124
-
125
- | Service Type | How to Verify |
126
- |--------------|---------------|
127
- | **Web frontend** | UI flows (navigate, wait_for, screenshot) |
128
- | **API/Worker** | UI flows that hit API endpoints |
129
- | **CLI/Batch** | Backend flows with golden inputs |
130
- | **Analysis pipeline** | Backend flows with known PR input |
131
-
132
- ### Backend Verification (CLI tools, analysis pipelines, batch jobs)
133
-
134
- **Not everything is a web page.** For CLI tools and batch processes, you need:
135
-
136
- 1. **Golden input** - A known-good input that produces predictable output
137
- 2. **Run command** - How to execute the service
138
- 3. **Output assertion** - What to check in the output
139
-
140
- ```json
141
- {
142
- "name": "Backend pipeline - golden input",
143
- "description": "Run pipeline on known input to verify it works",
144
- "trigger": "on_change",
145
- "watch_patterns": ["packages/my-pipeline/src/**"],
146
- "service": "my-pipeline",
147
- "type": "backend",
148
- "steps": [
149
- {
150
- "action": "run",
151
- "command": "pnpm start",
152
- "env": { "INPUT_ID": "known-good-input-123" },
153
- "timeout": 120
154
- },
155
- { "action": "assert_exit_code", "code": 0 },
156
- { "action": "assert_output_contains", "pattern": "Processing complete" }
157
- ]
158
- }
159
- ```
160
-
161
- ---
162
-
163
- ## STOP - Confirm Golden URLs and Golden Data
164
-
165
- **If you identified backend services that need verification, you MUST ask the user with a proposed example:**
166
-
167
- First, explore the codebase to find:
168
- - Example inputs used in tests, scripts, or documentation
169
- - Default/demo values in config files or environment variables
170
- - IDs or URLs referenced in comments or READMEs
171
-
172
- Then propose a specific example:
173
-
174
- > I found these backend services that need verification:
175
- > - **[service name]**: [what it does]
176
- >
177
- > For backend verification, I need **golden test data** - stable inputs that produce predictable output.
178
- >
179
- > **Based on what I found in the codebase, here's my suggestion:**
180
- > - **Golden URL/Input**: `[specific example you found, e.g., a test ID, demo endpoint, or sample file]`
181
- > - **Command**: `[the command to run with this input]`
182
- > - **Expected Output**: `[what success looks like based on the code]`
183
- >
184
- > Does this look right? Or should I use different test data?
185
-
186
- **Wait for the user to confirm or provide alternatives before adding backend flows.**
187
-
188
- ---
189
-
190
- ## Step 4: Assess Data Needs
191
-
192
- ```bash
193
- # Find API calls
194
- grep -r "fetch(\|useQuery\|useSWR\|axios" src/ --include="*.tsx" --include="*.ts" | wc -l
195
-
196
- # Find dynamic routes
197
- grep -r "useParams\|router.query\|\[.*\]" src/ --include="*.tsx" | head -10
198
-
199
- # Find external domains
200
- grep -r "https://" src/ --include="*.ts" --include="*.tsx" | grep -v node_modules | head -10
201
- ```
202
-
203
- ---
204
-
205
- ## STOP - Ask About Auth Requirements
206
-
207
- **You MUST ask the user before proceeding with fixtures or flows:**
208
-
209
- > Does this app require signed-in users to access the main features?
210
- >
211
- > Looking at the codebase, I see [authentication code, protected routes, user state, etc.].
212
- >
213
- > **How should verification handle authentication?**
214
- >
215
- > | Strategy | Best For | Complexity |
216
- > |----------|----------|------------|
217
- > | **Bypass** | Dev mode skips auth entirely | ⭐ Simplest |
218
- > | **Fixture** | Mock auth endpoints return fake user | ⭐⭐ Easy |
219
- > | **Token** | Inject real session token (you provide) | ⭐⭐⭐ Medium |
220
- > | **Test Account** | Full login flow with test credentials | ⭐⭐⭐⭐ Complex |
221
- >
222
- > 1. **Bypass** - Set env var like `SKIP_AUTH=true` (does your app support this?)
223
- > 2. **Fixture** - I'll create a mock `/auth/status` that returns a fake user
224
- > 3. **Token** - You provide a session cookie/token value to inject
225
- > 4. **Test Account** - Automated login with username/password (most realistic)
226
-
227
- **Wait for the user's response, then configure auth accordingly.**
228
-
229
- ### Configuring Auth Based on User Response
230
-
231
- **Bypass (simplest):**
232
- ```json
233
- {
234
- "auth": {
235
- "strategy": "bypass",
236
- "env": { "VITE_SKIP_AUTH": "true" }
237
- }
238
- }
239
- ```
240
-
241
- **Fixture (mock auth endpoint):**
242
- ```json
243
- {
244
- "auth": {
245
- "strategy": "fixture",
246
- "env": { "VITE_SKIP_AUTH": "true" },
247
- "fixture": {
248
- "pattern": "/auth/status",
249
- "source": "file://fixtures/auth-status.json"
250
- }
251
- }
252
- }
253
- ```
254
-
255
- Create `fixtures/auth-status.json`:
256
- ```json
257
- {
258
- "authenticated": true,
259
- "user": {
260
- "id": "test-user-123",
261
- "email": "test@example.com",
262
- "name": "Test User"
263
- }
264
- }
265
- ```
266
-
267
- **Token (inject session):**
268
- ```json
269
- {
270
- "auth": {
271
- "strategy": "token",
272
- "token": {
273
- "type": "cookie",
274
- "name": "session",
275
- "value": "$SESSION_TOKEN"
276
- }
277
- }
278
- }
279
- ```
280
-
281
- The `$SESSION_TOKEN` is resolved from Haystack secrets. Tell the user:
282
- > To use token auth, you'll need to add your session value as a secret:
283
- > ```bash
284
- > npx @haystackeditor/cli secrets set SESSION_TOKEN "your-session-value"
285
- > ```
286
-
287
- **Test Account (full login flow):**
288
- ```json
289
- {
290
- "auth": {
291
- "strategy": "test_account",
292
- "test_account": {
293
- "username": "$TEST_USERNAME",
294
- "password": "$TEST_PASSWORD",
295
- "login_url": "/login",
296
- "steps": [
297
- { "action": "type", "selector": "input[name=email]", "value": "$TEST_USERNAME" },
298
- { "action": "type", "selector": "input[name=password]", "value": "$TEST_PASSWORD" },
299
- { "action": "click", "selector": "button[type=submit]" }
300
- ]
301
- }
302
- }
303
- }
304
- ```
305
-
306
- ---
307
-
308
- ## STOP - Ask About Database Requirements
309
-
310
- **You MUST ask the user if the app needs database access:**
311
-
312
- > Does this app need a real database for verification?
313
- >
314
- > Looking at the codebase, I see [Prisma/Drizzle/Knex/raw SQL queries/etc.].
315
- >
316
- > **How should verification handle database access?**
317
- >
318
- > | Strategy | Best For | Complexity |
319
- > |----------|----------|------------|
320
- > | **Fixture** | Mock API responses (no real DB needed) | ⭐ Simplest |
321
- > | **Memory** | In-memory SQLite for read-heavy apps | ⭐⭐ Easy |
322
- > | **Seed** | File-based DB with seed data | ⭐⭐⭐ Medium |
323
- > | **Docker** | Spin up Postgres/MySQL/Redis containers | ⭐⭐⭐⭐ Complex |
324
- > | **Remote** | Connect to read-only staging database | ⭐⭐⭐⭐⭐ Advanced |
325
- >
326
- > For most apps, **fixtures are sufficient** — they mock the API layer so no real DB is needed.
327
-
328
- **Wait for the user's response, then configure database accordingly.**
329
-
330
- ### Configuring Database Based on User Response
331
-
332
- **Fixture (simplest - mock API responses):**
333
- ```json
334
- {
335
- "fixtures": [
336
- { "pattern": "/api/users", "source": "file://fixtures/users.json" },
337
- { "pattern": "/api/posts/*", "source": "file://fixtures/posts.json" }
338
- ]
339
- }
340
- ```
341
-
342
- **Memory (in-memory SQLite):**
343
- ```json
344
- {
345
- "database": {
346
- "strategy": "memory",
347
- "env": { "DATABASE_URL": "sqlite::memory:" }
348
- }
349
- }
350
- ```
351
-
352
- **Seed (file-based with seed data):**
353
- ```json
354
- {
355
- "database": {
356
- "strategy": "seed",
357
- "env": { "DATABASE_URL": "file:./test.db" },
358
- "seed_command": "pnpm db:seed"
359
- }
360
- }
361
- ```
362
-
363
- **Docker (real database containers):**
364
- ```json
365
- {
366
- "database": {
367
- "strategy": "docker",
368
- "services": [
369
- {
370
- "image": "postgres:15-alpine",
371
- "port": 5432,
372
- "env": {
373
- "POSTGRES_USER": "test",
374
- "POSTGRES_PASSWORD": "test",
375
- "POSTGRES_DB": "testdb"
376
- },
377
- "health_check": "pg_isready -U test",
378
- "health_timeout": 30
379
- }
380
- ],
381
- "env": { "DATABASE_URL": "postgresql://test:test@localhost:5432/testdb" },
382
- "migrate_command": "pnpm prisma migrate deploy",
383
- "seed_command": "pnpm db:seed"
384
- }
385
- }
386
- ```
387
-
388
- **Remote (read-only staging database):**
389
- ```json
390
- {
391
- "database": {
392
- "strategy": "remote",
393
- "env": { "DATABASE_URL": "$STAGING_DATABASE_URL" },
394
- "read_only": true
395
- }
396
- }
397
- ```
398
-
399
- The `$STAGING_DATABASE_URL` is resolved from Haystack secrets. Tell the user:
400
- > To use a remote database, add the connection string as a secret:
401
- > ```bash
402
- > npx @haystackeditor/cli secrets set STAGING_DATABASE_URL "postgresql://..."
403
- > ```
404
-
405
- **Fallback strategies** (try multiple approaches):
406
- ```json
407
- {
408
- "database": {
409
- "strategy": "memory",
410
- "env": { "DATABASE_URL": "sqlite::memory:" },
411
- "fallback": {
412
- "strategy": "seed",
413
- "env": { "DATABASE_URL": "file:./test.db" },
414
- "seed_command": "pnpm db:seed"
415
- }
416
- }
417
- }
418
- ```
419
-
420
- ---
421
-
422
- ## STOP - Ask About Test Data Strategy
423
-
424
- **You MUST ask the user before proceeding:**
425
-
426
- > I analyzed the codebase and found:
427
- > - X API fetch calls
428
- > - Y dynamic routes with parameters
429
- > - These external domains: [list them]
430
- >
431
- > **How should I handle test data for verification?**
432
- >
433
- > 1. **Passthrough** - Let API calls through to real servers (add domains to `network.allow`)
434
- > 2. **Staging URL** - Point flows at your staging/demo environment
435
- > 3. **Local fixtures** - JSON files in `fixtures/` directory
436
- > 4. **Skip data pages** - Only test static pages that don't need API data
437
-
438
- **Wait for the user's response before continuing.**
439
-
440
- ---
441
-
442
- ## STOP - Confirm Frontend Golden URLs
443
-
444
- **Goldens are URLs with real staging data** - they let verification test pages that need actual content to render properly. You need MULTIPLE goldens to cover different scenarios.
445
-
446
- **Before writing flows for dynamic routes, explore the codebase to find example data:**
447
-
448
- 1. Check test files (`*.test.ts`, `cypress/`, `playwright/`) for example IDs/URLs
449
- 2. Check README/docs for demo links or example usage
450
- 3. Check seed data, fixtures, or mock files
451
- 4. Check `.env.example` or config for staging URLs
452
- 5. Check existing routes to understand the URL format
453
-
454
- **Then propose MULTIPLE golden URLs (aim for 3-5) covering different scenarios:**
455
-
456
- > I found these dynamic routes that need staging data to render:
457
- >
458
- > **[Route Name]** (`/path/:param1/:param2`)
459
- >
460
- > | Golden | URL | Why |
461
- > |--------|-----|-----|
462
- > | Large dataset | `/path/example/large-123` | Tests performance with many items |
463
- > | Typical case | `/path/example/medium-456` | Common user scenario |
464
- > | Edge case | `/path/example/empty-789` | Tests empty/minimal state |
465
- > | Error state | `/path/example/error-000` | Tests error handling UI |
466
- >
467
- > **Evidence:**
468
- > - Found `large-123` in `cypress/fixtures/test-data.json`
469
- > - Found `medium-456` in README as demo example
470
- > - Found `empty-789` in `src/tests/edge-cases.test.ts`
471
- >
472
- > Does this look right?
473
- > 1. ✅ Yes, use these goldens
474
- > 2. 🔄 Use different URLs (I'll provide them)
475
-
476
- **Add goldens to `verification.goldens` in `.haystack.json` using this EXACT format:**
477
-
478
- ```json
479
- {
480
- "verification": {
481
- "goldens": {
482
- "frontend": [
483
- {
484
- "id": "large-dataset",
485
- "description": "100+ items - tests rendering performance",
486
- "route": "/the/actual/url/with/data"
487
- },
488
- {
489
- "id": "typical-case",
490
- "description": "Standard 10-20 items",
491
- "route": "/another/url/with/typical/data"
492
- },
493
- {
494
- "id": "empty-state",
495
- "description": "No items - tests empty state UI",
496
- "route": "/url/that/shows/empty/state"
497
- }
498
- ],
499
- "backend": [
500
- {
501
- "id": "golden-input-1",
502
- "description": "Known-good input for pipeline testing",
503
- "input": { "type": "github_pr", "value": "owner/repo#123" }
504
- }
505
- ]
506
- },
507
- "commands": [...]
508
- }
509
- }
510
- ```
511
-
512
- **DO NOT:**
513
- - Ask the user what format to use - USE THIS FORMAT
514
- - Put goldens at the top level - they go inside `verification.goldens`
515
- - Use a flat object like `{ "goldens": { "name": "url" } }` - use the array format above
516
- - Embed golden URLs only in flow steps - also add them to the goldens section for discoverability
517
-
518
- **CRITICAL**:
519
- - Propose MULTIPLE goldens (3-5), not just one
520
- - Cover different scenarios: large, typical, empty, error states
521
- - Always propose SPECIFIC URLs, never ask the user to freestyle
522
- - Show WHERE you found the example data (file path, line if possible)
523
- - Make sure URLs match the app's actual route format (check `<Route path=`)
524
- - Wait for confirmation before writing flows with these URLs
525
-
526
- ---
527
-
528
- ## Step 5: Write Flows
529
-
530
- Flows tell the Planner about your app's routes, UI elements, and user journeys.
531
-
532
- ### Structure
533
-
534
- ```yaml
535
- flows:
536
- - name: "Descriptive name of what this tests"
537
- trigger: always # or on_change with watch_patterns
538
- steps:
539
- - action: navigate
540
- url: "/"
541
- - action: wait_for
542
- selector: "[data-testid='specific-element']"
543
- - action: click
544
- selector: "[data-testid='button']"
545
- - action: screenshot
546
- name: "result"
547
- ```
548
-
549
- ### What to Include
550
-
551
- **Core journey flow** (trigger: always):
552
- - The main thing users do in your app
553
- - Multiple steps with interactions (click, type)
554
- - Waits for meaningful state changes
555
-
556
- **Route coverage flows**:
557
- - One flow per major route
558
- - Uses specific selectors the Planner can learn from
559
- - `watch_patterns` to only run when relevant files change
560
-
561
- ### Finding Good Selectors
562
-
563
- ```bash
564
- # Find data-testid attributes
565
- grep -r "data-testid" src/ --include="*.tsx" | head -20
566
-
567
- # Find aria-labels
568
- grep -r "aria-label" src/ --include="*.tsx" | head -20
569
-
570
- # Find component class names
571
- grep -r "className=" src/components/ --include="*.tsx" | head -20
572
- ```
573
-
574
- Use specific selectors like:
575
- - `[data-testid='dashboard-chart']`
576
- - `[aria-label='Submit form']`
577
- - `.pricing-table`
578
- - `button[type='submit']`
579
-
580
- If good selectors don't exist, add `data-testid` to key components.
581
-
582
- ### watch_patterns
583
-
584
- For flows that only matter when certain files change:
585
-
586
- ```yaml
587
- - name: "Settings page works"
588
- trigger: on_change
589
- watch_patterns:
590
- - "src/pages/settings/**"
591
- - "src/components/settings/**"
592
- steps:
593
- - action: navigate
594
- url: "/settings"
595
- # ...
596
- ```
597
-
598
- ---
599
-
600
- ## Step 6: Configure Fixtures (if needed)
601
-
602
- Based on user's answer in Step 3:
603
-
604
- **Passthrough**:
605
- ```yaml
606
- network:
607
- allow:
608
- - "api.example.com"
609
- - "cdn.example.com"
610
- ```
611
-
612
- **Staging URL**:
613
- ```yaml
614
- flows:
615
- - name: "Dashboard with real data"
616
- steps:
617
- - action: navigate
618
- url: "https://staging.example.com/dashboard"
619
- ```
620
-
621
- **Local fixtures**:
622
- ```yaml
623
- fixtures:
624
- - pattern: "/api/user"
625
- source: "file://fixtures/user.json"
626
- - pattern: "/api/data/*"
627
- source: "file://fixtures/data.json"
628
- ```
629
-
630
- ---
631
-
632
- ## Step 7: Commit
633
-
634
- ```bash
635
- git add .haystack.json fixtures/
636
- git commit -m "Add Haystack verification"
637
- ```
638
-
639
- Done! The Planner will use your flows to understand the app and create verification plans for PRs.
@@ -1,42 +0,0 @@
1
- # Haystack Verification
2
-
3
- This project uses Haystack for PR verification. Configuration is in `.haystack.yml`.
4
-
5
- ## For the AI Agent
6
-
7
- When making changes to this codebase:
8
-
9
- 1. **After completing changes**, run verification:
10
- - Use the `haystack_get_verify_targets` tool to see what URLs to check
11
- - Use `haystack_get_auth_config` to get auth bypass instructions
12
- - Start the dev server with auth bypass env vars
13
- - Navigate to each verification URL and confirm it loads
14
-
15
- 2. **If `.haystack.yml` doesn't exist**, help the user create one:
16
- - Use `haystack_setup_start` to begin the wizard
17
- - Ask each question and call `haystack_setup_answer` with responses
18
-
19
- 3. **Fixture data** is available via `haystack_load_fixtures`:
20
- - This loads test data from staging/local files
21
- - Use this instead of real API calls when verifying
22
-
23
- ## Quick Reference
24
-
25
- ```yaml
26
- # .haystack.yml structure
27
- dev:
28
- command: pnpm dev # How to start dev server
29
- port: 3000 # What port it runs on
30
-
31
- auth:
32
- strategy: bypass # How to skip auth
33
- env: "SKIP_AUTH=true" # Env var to set
34
-
35
- fixtures: # Test data sources
36
- - pattern: "/api/*"
37
- source: "https://staging.example.com/api"
38
-
39
- verify: # Pages to check
40
- - url: "/"
41
- wait_for: "#app"
42
- ```