@haystackeditor/cli 0.6.0 → 0.7.1

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.
@@ -27,7 +27,12 @@ export async function detectProject(rootDir = process.cwd()) {
27
27
  }
28
28
  // Detect framework
29
29
  result.framework = await detectFramework(rootDir);
30
- // Set suggestions based on framework
30
+ // Extract custom port from config if framework supports it
31
+ const customPort = await extractPortFromConfig(rootDir, result.framework);
32
+ if (customPort) {
33
+ result.suggestedPort = customPort;
34
+ }
35
+ // Set suggestions based on framework (won't override customPort if set)
31
36
  setSuggestions(result);
32
37
  // Detect auth bypass from .env.example
33
38
  result.suggestedAuthBypass = await detectAuthBypass(rootDir);
@@ -188,12 +193,14 @@ async function detectService(rootDir, servicePath) {
188
193
  else if (await hasFile(fullPath, 'vite.config.ts') || await hasFile(fullPath, 'vite.config.js')) {
189
194
  framework = 'vite';
190
195
  suggestedCommand = 'pnpm dev';
191
- suggestedPort = 5173;
196
+ // Try to extract custom port from vite config, fallback to default
197
+ suggestedPort = await extractPortFromConfig(fullPath, 'vite') ?? 5173;
192
198
  }
193
199
  else if (await hasFile(fullPath, 'next.config.js') || await hasFile(fullPath, 'next.config.ts')) {
194
200
  framework = 'nextjs';
195
201
  suggestedCommand = 'pnpm dev';
196
- suggestedPort = 3000;
202
+ // Try to extract custom port from next config, fallback to default
203
+ suggestedPort = await extractPortFromConfig(fullPath, 'nextjs') ?? 3000;
197
204
  }
198
205
  else if (scripts.dev) {
199
206
  suggestedCommand = 'pnpm dev';
@@ -280,6 +287,7 @@ async function detectAuthBypass(rootDir) {
280
287
  }
281
288
  /**
282
289
  * Set suggestions based on detected framework
290
+ * Note: This is sync, so custom port extraction happens in detectProject
283
291
  */
284
292
  function setSuggestions(result) {
285
293
  const frameworkDefaults = {
@@ -294,7 +302,8 @@ function setSuggestions(result) {
294
302
  };
295
303
  const defaults = result.framework ? frameworkDefaults[result.framework] : null;
296
304
  result.suggestedDevCommand = defaults?.command || `${result.packageManager} dev`;
297
- result.suggestedPort = defaults?.port || 3000;
305
+ // suggestedPort may already be set from custom config extraction
306
+ result.suggestedPort = result.suggestedPort || defaults?.port || 3000;
298
307
  result.suggestedReadyPattern = defaults?.ready || 'ready|started|listening|Local:';
299
308
  }
300
309
  /**
@@ -309,6 +318,58 @@ async function hasFile(dir, file) {
309
318
  return false;
310
319
  }
311
320
  }
321
+ /**
322
+ * Extract custom port from framework config files
323
+ *
324
+ * Parses vite.config.ts, next.config.js, etc. to find custom port settings
325
+ * Returns undefined if no custom port is configured (use framework default)
326
+ */
327
+ async function extractPortFromConfig(rootDir, framework) {
328
+ if (!framework)
329
+ return undefined;
330
+ // Config files to check based on framework
331
+ const configFiles = {
332
+ vite: ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'],
333
+ nextjs: ['next.config.js', 'next.config.ts', 'next.config.mjs'],
334
+ nuxt: ['nuxt.config.ts', 'nuxt.config.js'],
335
+ sveltekit: ['svelte.config.js', 'svelte.config.ts'],
336
+ astro: ['astro.config.mjs', 'astro.config.ts'],
337
+ };
338
+ const files = configFiles[framework];
339
+ if (!files)
340
+ return undefined;
341
+ let configContent = null;
342
+ for (const file of files) {
343
+ try {
344
+ configContent = await fs.readFile(path.join(rootDir, file), 'utf-8');
345
+ break;
346
+ }
347
+ catch {
348
+ // File not found, try next
349
+ }
350
+ }
351
+ if (!configContent)
352
+ return undefined;
353
+ // Patterns to match port configuration
354
+ // Vite: server: { port: 3000 } or server.port = 3000
355
+ // Next: env: { PORT: 3000 } or experimental.serverPort
356
+ const patterns = [
357
+ /server\s*:\s*\{[^}]*port\s*:\s*(\d+)/s, // server: { port: 3000 }
358
+ /server\s*\.\s*port\s*[=:]\s*(\d+)/, // server.port = 3000
359
+ /port\s*:\s*(\d+)/, // port: 3000 (generic)
360
+ /PORT\s*[=:]\s*['"]?(\d+)['"]?/, // PORT = 3000
361
+ ];
362
+ for (const pattern of patterns) {
363
+ const match = configContent.match(pattern);
364
+ if (match) {
365
+ const port = parseInt(match[1], 10);
366
+ if (port > 0 && port < 65536) {
367
+ return port;
368
+ }
369
+ }
370
+ }
371
+ return undefined;
372
+ }
312
373
  /**
313
374
  * Detect proxy dependencies from vite.config.ts/js
314
375
  *
@@ -50,7 +50,75 @@ Your flows should describe THIS journey, not just "pages load".
50
50
 
51
51
  ---
52
52
 
53
- ## Step 3: Assess Data Needs
53
+ ## Step 3: Verify ALL Services Have Flows (CRITICAL)
54
+
55
+ **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.
56
+
57
+ ### Check for Uncovered Services
58
+
59
+ \`\`\`bash
60
+ # List all services
61
+ grep -A1 '"services"' .haystack.json | grep -E '^\\s+"[^"]+":' | sed 's/.*"\\([^"]*\\)".*/\\1/'
62
+
63
+ # List all flows
64
+ grep '"name":' .haystack.json
65
+
66
+ # MANUAL CHECK: Does every service have a flow?
67
+ \`\`\`
68
+
69
+ ### Types of Services
70
+
71
+ | Service Type | How to Verify |
72
+ |--------------|---------------|
73
+ | **Web frontend** | UI flows (navigate, wait_for, screenshot) |
74
+ | **API/Worker** | UI flows that hit API endpoints |
75
+ | **CLI/Batch** | Backend flows with golden inputs |
76
+ | **Analysis pipeline** | Backend flows with known PR input |
77
+
78
+ ### Backend Verification (CLI tools, analysis pipelines, batch jobs)
79
+
80
+ **Not everything is a web page.** For CLI tools and batch processes, you need:
81
+
82
+ 1. **Golden input** - A known-good input that produces predictable output
83
+ 2. **Run command** - How to execute the service
84
+ 3. **Output assertion** - What to check in the output
85
+
86
+ \`\`\`json
87
+ {
88
+ "name": "Analysis pipeline - golden PR",
89
+ "description": "Run analysis on known PR to verify pipeline works",
90
+ "trigger": "on_change",
91
+ "watch_patterns": ["analysis/src/**", "analysis/package.json"],
92
+ "service": "haystack-analysis",
93
+ "type": "backend",
94
+ "steps": [
95
+ {
96
+ "action": "run",
97
+ "command": "pnpm start",
98
+ "env": { "PR_IDENTIFIER": "owner/repo#123" },
99
+ "timeout": 120
100
+ },
101
+ { "action": "assert_exit_code", "code": 0 },
102
+ { "action": "assert_output_contains", "pattern": "Analysis complete" }
103
+ ]
104
+ }
105
+ \`\`\`
106
+
107
+ ### Finding Golden Inputs
108
+
109
+ For analysis/processing pipelines:
110
+ - Pick a **small, stable PR** that won't change
111
+ - Document what the expected output should be
112
+ - Add it as a comment in the flow description
113
+
114
+ \`\`\`bash
115
+ # Find a good golden PR (small, merged, stable)
116
+ gh pr list --state merged --limit 10 --json number,title,changedFiles | jq '.[] | select(.changedFiles < 5)'
117
+ \`\`\`
118
+
119
+ ---
120
+
121
+ ## Step 4: Assess Data Needs
54
122
 
55
123
  \`\`\`bash
56
124
  # Find API calls
@@ -85,7 +153,7 @@ grep -r "https://" src/ --include="*.ts" --include="*.tsx" | grep -v node_module
85
153
 
86
154
  ---
87
155
 
88
- ## Step 4: Write Flows
156
+ ## Step 5: Write Flows
89
157
 
90
158
  Flows tell the Planner about your app's routes, UI elements, and user journeys.
91
159
 
@@ -157,7 +225,7 @@ For flows that only matter when certain files change:
157
225
 
158
226
  ---
159
227
 
160
- ## Step 5: Configure Fixtures (if needed)
228
+ ## Step 6: Configure Fixtures (if needed)
161
229
 
162
230
  Based on user's answer in Step 3:
163
231
 
@@ -189,7 +257,7 @@ fixtures:
189
257
 
190
258
  ---
191
259
 
192
- ## Step 6: Commit
260
+ ## Step 7: Commit
193
261
 
194
262
  \`\`\`bash
195
263
  git add .haystack.json fixtures/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haystackeditor/cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Set up Haystack verification for your project",
5
5
  "type": "module",
6
6
  "bin": {