@zibby/cli 0.1.26 → 0.1.27

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 (46) hide show
  1. package/README.md +44 -44
  2. package/dist/auth/cli-login.js +18 -0
  3. package/dist/bin/zibby.js +2 -0
  4. package/dist/commands/agent-reliability.js +8 -0
  5. package/dist/commands/analyze-graph.js +18 -0
  6. package/dist/commands/chat-session-store.js +1 -0
  7. package/dist/commands/chat.js +79 -0
  8. package/dist/commands/ci-setup.js +18 -0
  9. package/dist/commands/generate.js +69 -0
  10. package/dist/commands/implement.js +65 -0
  11. package/dist/commands/init.js +344 -0
  12. package/dist/commands/list-projects.js +11 -0
  13. package/dist/commands/memory.js +39 -0
  14. package/dist/commands/run-capacity-queue-cli.js +4 -0
  15. package/dist/commands/run.js +112 -0
  16. package/dist/commands/setup-scripts.js +15 -0
  17. package/dist/commands/studio.js +33 -0
  18. package/dist/commands/upload.js +22 -0
  19. package/dist/commands/video.js +6 -0
  20. package/dist/commands/workflow.js +45 -0
  21. package/dist/config/config.js +1 -0
  22. package/dist/config/environments.js +1 -0
  23. package/dist/utils/chat-run-lifecycle.js +3 -0
  24. package/dist/utils/execution-context.js +1 -0
  25. package/dist/utils/progress-reporter.js +1 -0
  26. package/dist/utils/studio-cli-log-mirror.js +1 -0
  27. package/dist/utils/studio-installer.js +7 -0
  28. package/dist/utils/studio-launcher.js +1 -0
  29. package/package.json +19 -16
  30. package/bin/zibby.js +0 -273
  31. package/src/auth/cli-login.js +0 -404
  32. package/src/commands/analyze-graph.js +0 -336
  33. package/src/commands/ci-setup.js +0 -65
  34. package/src/commands/implement.js +0 -664
  35. package/src/commands/init.js +0 -770
  36. package/src/commands/list-projects.js +0 -77
  37. package/src/commands/memory.js +0 -171
  38. package/src/commands/run.js +0 -919
  39. package/src/commands/setup-scripts.js +0 -114
  40. package/src/commands/upload.js +0 -162
  41. package/src/commands/video.js +0 -30
  42. package/src/commands/workflow.js +0 -368
  43. package/src/config/config.js +0 -117
  44. package/src/config/environments.js +0 -145
  45. package/src/utils/execution-context.js +0 -25
  46. package/src/utils/progress-reporter.js +0 -155
@@ -1,770 +0,0 @@
1
- import { mkdir, writeFile, readFile } from 'fs/promises';
2
- import { existsSync } from 'fs';
3
- import { join, resolve, dirname } from 'path';
4
- import { homedir } from 'os';
5
- import inquirer from 'inquirer';
6
- import chalk from 'chalk';
7
- import ora from 'ora';
8
- import { spawn, execSync } from 'child_process';
9
- import { fileURLToPath } from 'url';
10
-
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = dirname(__filename);
13
-
14
- async function checkNpmGlobalBinInPath() {
15
- try {
16
- const isWindows = process.platform === 'win32';
17
-
18
- // Get npm global bin directory
19
- const npmPrefix = execSync('npm config get prefix', { encoding: 'utf-8' }).trim();
20
- const npmGlobalBin = isWindows ? npmPrefix : `${npmPrefix}/bin`;
21
- const npmGlobalBinExpanded = npmGlobalBin.replace(/^~/, homedir());
22
-
23
- // Check if it's in PATH
24
- const pathSeparator = isWindows ? ';' : ':';
25
- const pathDirs = process.env.PATH.split(pathSeparator);
26
- const isInPath = pathDirs.some(dir => {
27
- const normalizedDir = dir.replace(/^~/, homedir());
28
- return normalizedDir === npmGlobalBinExpanded || normalizedDir === npmGlobalBin;
29
- });
30
-
31
- if (isInPath) {
32
- return; // Already configured
33
- }
34
-
35
- // Not in PATH - offer to add it
36
- console.log(chalk.yellow('⚠️ npm global bin not in PATH'));
37
- console.log(chalk.gray(` Location: ${npmGlobalBinExpanded}`));
38
- console.log();
39
-
40
- const { shouldAddPath } = await inquirer.prompt([{
41
- type: 'confirm',
42
- name: 'shouldAddPath',
43
- message: 'Add npm global bin to your shell PATH automatically?',
44
- default: true
45
- }]);
46
-
47
- if (!shouldAddPath) {
48
- if (isWindows) {
49
- console.log(chalk.gray('\n💡 To add manually on Windows:'));
50
- console.log(chalk.gray(' 1. Search "Environment Variables" in Start menu'));
51
- console.log(chalk.gray(' 2. Edit "Path" in User variables'));
52
- console.log(chalk.gray(` 3. Add: ${npmGlobalBinExpanded}\n`));
53
- } else {
54
- console.log(chalk.gray('\n💡 To add manually, run:'));
55
- console.log(chalk.gray(` echo 'export PATH="${npmGlobalBinExpanded}:$PATH"' >> ~/.zshrc`));
56
- console.log(chalk.gray(` source ~/.zshrc\n`));
57
- }
58
- return;
59
- }
60
-
61
- // Windows: Can't auto-add to PATH (requires registry changes)
62
- if (isWindows) {
63
- console.log(chalk.yellow('⚠️ Cannot auto-add PATH on Windows'));
64
- console.log(chalk.gray(' Please add manually:'));
65
- console.log(chalk.gray(' 1. Search "Environment Variables" in Start menu'));
66
- console.log(chalk.gray(' 2. Edit "Path" in User variables'));
67
- console.log(chalk.gray(` 3. Add: ${npmGlobalBinExpanded}\n`));
68
- return;
69
- }
70
-
71
- // Unix/Linux: Auto-add to shell config
72
- const shell = process.env.SHELL || '';
73
- let shellRc = '';
74
-
75
- if (shell.includes('zsh')) {
76
- shellRc = join(homedir(), '.zshrc');
77
- } else if (shell.includes('bash')) {
78
- // Linux often uses .bashrc, macOS uses .bash_profile
79
- shellRc = existsSync(join(homedir(), '.bashrc'))
80
- ? join(homedir(), '.bashrc')
81
- : join(homedir(), '.bash_profile');
82
- } else {
83
- console.log(chalk.yellow(`⚠️ Unknown shell: ${shell}`));
84
- console.log(chalk.gray(' Please add manually:'));
85
- console.log(chalk.gray(` export PATH="${npmGlobalBinExpanded}:$PATH"`));
86
- return;
87
- }
88
-
89
- // Check if already in shell config
90
- if (existsSync(shellRc)) {
91
- const rcContent = await readFile(shellRc, 'utf-8');
92
- if (rcContent.includes(npmGlobalBinExpanded) || (rcContent.includes('npm') && rcContent.includes('global') && rcContent.includes('bin'))) {
93
- console.log(chalk.yellow(`⚠️ PATH entry found in ${shellRc} but not active`));
94
- console.log(chalk.gray(` Run: source ${shellRc}\n`));
95
- return;
96
- }
97
- }
98
-
99
- // Add to shell config
100
- const pathEntry = `\n# npm global bin (added by zibby)\nexport PATH="${npmGlobalBinExpanded}:$PATH"\n`;
101
- await writeFile(shellRc, (existsSync(shellRc) ? await readFile(shellRc, 'utf-8') : '') + pathEntry);
102
-
103
- console.log(chalk.green(`✅ Added to ${shellRc}`));
104
- console.log(chalk.yellow('\n💡 Run this to activate in current session:'));
105
- console.log(chalk.gray(` source ${shellRc}\n`));
106
-
107
- } catch {
108
- // Silently fail - don't block init if this check fails
109
- }
110
- }
111
-
112
- export async function initCommand(projectName, options) {
113
- console.log(chalk.bold.cyan('\n🎭 Welcome to Zibby Test Automation!\n'));
114
-
115
- // Check if npm global bin is in PATH
116
- await checkNpmGlobalBinInPath();
117
-
118
- const targetDir = projectName ? resolve(process.cwd(), projectName) : process.cwd();
119
- const projectNameActual = projectName || 'zibby-tests';
120
- const isNewProject = !!projectName;
121
-
122
- // If creating new project, check if directory exists
123
- if (isNewProject && existsSync(targetDir)) {
124
- console.log(chalk.red(`\n❌ Directory "${projectName}" already exists!\n`));
125
- process.exit(1);
126
- }
127
-
128
- // If in existing directory, check if already initialized (unless --force)
129
- if (!isNewProject && existsSync(join(targetDir, '.zibby.config.mjs')) && !options.force) {
130
- console.log(chalk.yellow('\n⚠️ Zibby is already initialized in this directory!\n'));
131
- console.log(chalk.white('Config file found: .zibby.config.mjs'));
132
- console.log(chalk.gray('Use --force or -f to reinitialize\n'));
133
- process.exit(0);
134
- }
135
-
136
- if (options.force && !isNewProject) {
137
- console.log(chalk.cyan('\nReinitializing Zibby configuration...\n'));
138
- }
139
-
140
- // Build answers from CLI flags or prompt interactively
141
- let answers;
142
-
143
- // Check if all options provided via CLI (non-interactive mode)
144
- const hasAllFlags = options.agent && (options.headed || options.headless);
145
-
146
- if (hasAllFlags) {
147
- // Non-interactive mode - use CLI flags
148
- console.log(chalk.cyan('Setting up with provided options...\n'));
149
- answers = {
150
- agent: options.agent,
151
- browserMode: options.headless ? 'headless' : 'headed',
152
- apiKey: options.apiKey || null,
153
- cloudSync: !!(options.cloudSync || options.apiKey),
154
- };
155
- } else {
156
- // Interactive mode - prompt for missing options
157
- const prompts = [];
158
-
159
- if (!options.agent) {
160
- prompts.push({
161
- type: 'select',
162
- name: 'agent',
163
- message: 'Which AI agent do you prefer?',
164
- choices: [
165
- { name: 'Cursor', value: 'cursor' },
166
- { name: 'Claude (Anthropic)', value: 'claude' },
167
- ],
168
- default: 'cursor',
169
- });
170
- }
171
-
172
- if (!options.headed && !options.headless) {
173
- prompts.push({
174
- type: 'select',
175
- name: 'browserMode',
176
- message: 'Browser mode during live AI execution?',
177
- choices: [
178
- { name: 'Headed - Visible browser (recommended for development)', value: 'headed' },
179
- { name: 'Headless - Hidden browser (for CI/CD)', value: 'headless' },
180
- ],
181
- default: 'headed',
182
- });
183
- }
184
-
185
- if (!options.apiKey) {
186
- prompts.push({
187
- type: 'input',
188
- name: 'apiKey',
189
- message: 'Enable cloud sync? Enter project ZIBBY_API_KEY (or press Enter to skip):',
190
- });
191
- }
192
-
193
- answers = prompts.length > 0 ? await inquirer.prompt(prompts) : {};
194
-
195
- // Merge with CLI options
196
- answers.agent = options.agent || answers.agent;
197
- answers.browserMode = options.headless ? 'headless' : (options.headed ? 'headed' : answers.browserMode);
198
- answers.apiKey = options.apiKey || answers.apiKey;
199
- answers.cloudSync = !!(options.cloudSync || options.apiKey || (answers.apiKey && answers.apiKey.trim()));
200
- }
201
-
202
- // Set Playwright as default (only option for now)
203
- answers.mcp = 'playwright';
204
-
205
- // Automatically setup MCP if Cursor agent selected
206
- answers.setupMcp = (answers.agent === 'cursor');
207
-
208
- const spinner = ora('Setting up Zibby...').start();
209
-
210
- try {
211
- // Create project directory if new project
212
- if (isNewProject) {
213
- await mkdir(targetDir, { recursive: true });
214
- }
215
-
216
- // Create folders
217
- await mkdir(join(targetDir, 'test-specs/examples'), { recursive: true });
218
- await mkdir(join(targetDir, 'tests'), { recursive: true });
219
- await mkdir(join(targetDir, '.zibby/output'), { recursive: true });
220
-
221
- if (options.mem) {
222
- try {
223
- const { initMemory, DoltDB } = await import('@zibby/memory');
224
- if (DoltDB.isAvailable()) {
225
- const { created } = initMemory(targetDir);
226
- if (created) {
227
- spinner.text = 'Initialized test memory database (Dolt)...';
228
- }
229
- } else {
230
- spinner.text = 'Dolt not found — skipping memory database (brew install dolt)';
231
- }
232
- } catch (_e) {
233
- // @zibby/memory not available — skip silently
234
- }
235
- }
236
-
237
- spinner.text = 'Scaffolding workflow graph...';
238
-
239
- // Use Template Factory to get the right template
240
- const { TemplateFactory } = await import('@zibby/core/templates');
241
- const templateName = options.template || 'browser-test-automation'; // Default to browser-test-automation
242
-
243
- try {
244
- const { graphPath, nodesPath, readmePath, resultHandlerPath } = TemplateFactory.getTemplateFiles(templateName);
245
- const targetZibbyDir = join(targetDir, '.zibby');
246
-
247
- // Copy graph.mjs
248
- const graphTemplate = await readFile(graphPath, 'utf-8');
249
- await writeFile(join(targetZibbyDir, 'graph.mjs'), graphTemplate);
250
-
251
- // Copy result-handler.mjs (workflow-specific post-processing)
252
- if (resultHandlerPath) {
253
- const rhTemplate = await readFile(resultHandlerPath, 'utf-8');
254
- await writeFile(join(targetZibbyDir, 'result-handler.mjs'), rhTemplate);
255
- }
256
-
257
- // Copy README
258
- const readmeTemplate = await readFile(readmePath, 'utf-8');
259
- await writeFile(join(targetZibbyDir, 'README.md'), readmeTemplate);
260
-
261
- // Copy nodes directory
262
- await mkdir(join(targetZibbyDir, 'nodes'), { recursive: true });
263
- const { readdirSync } = await import('fs');
264
- const nodeFiles = readdirSync(nodesPath);
265
- for (const file of nodeFiles) {
266
- let content = await readFile(join(nodesPath, file), 'utf-8');
267
-
268
- // If --mem flag NOT used, remove SKILLS.MEMORY from execute-live.mjs
269
- if (!options.mem && file === 'execute-live.mjs') {
270
- content = content.replace(
271
- 'skills: [SKILLS.BROWSER, SKILLS.MEMORY],',
272
- 'skills: [SKILLS.BROWSER],'
273
- );
274
- }
275
-
276
- await writeFile(join(targetZibbyDir, 'nodes', file), content);
277
- }
278
- } catch (error) {
279
- spinner.fail(`Failed to scaffold template: ${error.message}`);
280
- throw error;
281
- }
282
-
283
- spinner.text = 'Generating configuration files...';
284
-
285
- // Always create Zibby config
286
- const configContent = generateConfig(answers, options);
287
- await writeFile(join(targetDir, '.zibby.config.mjs'), configContent);
288
-
289
- // Create .env.example as a template (never auto-create .env)
290
- const envContent = answers.apiKey && answers.apiKey.trim()
291
- ? generateEnvFileWithKey(answers, answers.apiKey.trim())
292
- : generateEnvFile(answers);
293
- await writeFile(join(targetDir, '.env.example'), envContent);
294
-
295
-
296
- // Create package.json for new projects only (don't modify existing ones)
297
- if (isNewProject) {
298
- const packageJsonContent = generatePackageJson(projectNameActual, answers);
299
- await writeFile(join(targetDir, 'package.json'), packageJsonContent);
300
- }
301
-
302
- // Create .gitignore if doesn't exist
303
- if (!existsSync(join(targetDir, '.gitignore'))) {
304
- const gitignoreContent = generateGitignore();
305
- await writeFile(join(targetDir, '.gitignore'), gitignoreContent);
306
- }
307
-
308
- // Create playwright.config.js if doesn't exist
309
- if (!existsSync(join(targetDir, 'playwright.config.js'))) {
310
- const playwrightConfigContent = generatePlaywrightConfig('on'); // Default: video enabled
311
- await writeFile(join(targetDir, 'playwright.config.js'), playwrightConfigContent);
312
- }
313
-
314
- // Create example spec if doesn't exist
315
- if (!existsSync(join(targetDir, 'test-specs/examples/example-domain.txt'))) {
316
- const exampleSpecContent = generateExampleSpec();
317
- await writeFile(join(targetDir, 'test-specs/examples/example-domain.txt'), exampleSpecContent);
318
- }
319
-
320
- // Create README only for new projects
321
- if (isNewProject) {
322
- const readmeContent = generateReadme(projectNameActual, answers);
323
- await writeFile(join(targetDir, 'README.md'), readmeContent);
324
- }
325
-
326
- spinner.succeed(isNewProject ? 'Project created!' : 'Zibby initialized!');
327
-
328
- // Install dependencies for new projects
329
- if (isNewProject && !options.skipInstall) {
330
- const installSpinner = ora('Installing dependencies...').start();
331
-
332
- await new Promise((resolveFn, reject) => {
333
- const npm = spawn('npm', ['install'], {
334
- cwd: targetDir,
335
- stdio: 'pipe',
336
- });
337
-
338
- npm.on('close', (code) => {
339
- if (code === 0) {
340
- installSpinner.succeed('Dependencies installed!');
341
- resolveFn();
342
- } else {
343
- installSpinner.fail('Failed to install dependencies');
344
- reject(new Error('npm install failed'));
345
- }
346
- });
347
- });
348
- } else if (!isNewProject) {
349
- console.log(chalk.gray('\nMake sure @zibby/cli is installed in your package.json\n'));
350
- }
351
-
352
- // Always check and install Playwright browsers if missing (for both new and existing projects)
353
- // This ensures browsers are available even when reinitializing
354
- if (!options.skipInstall) {
355
- const playwrightSpinner = ora('Installing Playwright browsers...').start();
356
-
357
- await new Promise((resolveFn) => {
358
- const npx = spawn('npx', ['playwright', 'install', 'chromium'], {
359
- cwd: targetDir,
360
- stdio: 'pipe',
361
- });
362
-
363
- let output = '';
364
- npx.stdout.on('data', (data) => {
365
- output += data.toString();
366
- });
367
- npx.stderr.on('data', (data) => {
368
- output += data.toString();
369
- });
370
-
371
- npx.on('close', (code) => {
372
- if (code === 0) {
373
- // Check if it actually installed or was already installed
374
- if (output.includes('already installed') || output.includes('up to date')) {
375
- playwrightSpinner.succeed('Playwright browsers already installed');
376
- } else {
377
- playwrightSpinner.succeed('Playwright browsers installed!');
378
- }
379
- resolveFn();
380
- } else {
381
- playwrightSpinner.warn('Could not verify Playwright browsers');
382
- console.log(chalk.yellow('\n⚠️ If tests fail, run: npx playwright install\n'));
383
- resolveFn(); // Don't fail the whole init
384
- }
385
- });
386
- });
387
- }
388
-
389
- // Setup cursor-agent MCP if requested
390
- if (answers.agent === 'cursor' && answers.setupMcp) {
391
- const mcpSpinner = ora('Setting up Playwright MCP...').start();
392
-
393
- try {
394
- const { setupPlaywrightMcpCommand } = await import('./setup-scripts.js');
395
-
396
- // Use cloudSync from answers (prompt response or flag)
397
- const cloudSync = answers.cloudSync || false;
398
-
399
- // Use browserMode from answers (default to headed)
400
- const isHeaded = answers.browserMode !== 'headless';
401
-
402
- await setupPlaywrightMcpCommand({
403
- headed: isHeaded,
404
- cloudSync,
405
- video: 'on', // Default video recording enabled
406
- viewport: { width: 1280, height: 720 } // Default viewport size
407
- });
408
-
409
- let message = 'Playwright MCP configured';
410
- if (isHeaded) message += ' (headed mode - visible browser)';
411
- else message += ' (headless mode - hidden browser)';
412
- if (cloudSync) message += ' + Zibby MCP (cloud sync)';
413
-
414
- mcpSpinner.succeed(message);
415
-
416
- if (cloudSync) {
417
- console.log(chalk.gray('\n Copy .env.example to .env and set ZIBBY_API_KEY to enable uploads\n'));
418
- }
419
- } catch (error) {
420
- // Error during MCP setup - check if MCP is already configured
421
- mcpSpinner.fail('MCP setup script failed');
422
- console.log(chalk.yellow(' Check if MCP is already configured:'));
423
- console.log(chalk.gray(' • Open Cursor settings → Check "playwright-official" MCP'));
424
- console.log(chalk.gray(' • Run: cursor-agent mcp list'));
425
- console.log(chalk.gray(` • Or run manually: zibby setup-playwright\n`));
426
- console.log(chalk.gray(` Error: ${error.message}\n`));
427
- }
428
- }
429
-
430
- console.log(chalk.bold.green('\n🎉 All set!\n'));
431
- console.log(chalk.cyan('Try running your first test:'));
432
- if (isNewProject) {
433
- console.log(chalk.white(` cd ${projectName}`));
434
- }
435
- console.log(chalk.white(' zibby run test-specs/examples/example-domain.txt\n'));
436
-
437
- console.log(chalk.cyan('Next steps:'));
438
- let step = 1;
439
- console.log(chalk.white(` ${step++}. cp .env.example .env ${chalk.gray('# then add your API keys')}`));
440
- console.log(chalk.white(` ${step++}. Write test specs in test-specs/`));
441
- console.log(chalk.white(` ${step++}. Run: npx zibby run <spec-file>\n`));
442
-
443
- } catch (error) {
444
- spinner.fail('Failed to create project');
445
- console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
446
- process.exit(1);
447
- }
448
- }
449
-
450
- function generateConfig(answers, _options = {}) {
451
- // Import here to avoid circular deps - constants are pure values
452
- const DEFAULT_CLAUDE_MODEL = 'auto';
453
- return `export default {
454
- // AI agent settings - specify 'claude' or 'cursor' block
455
- agent: {${answers.agent === 'claude' ? `
456
- // Claude API settings
457
- claude: {
458
- model: '${DEFAULT_CLAUDE_MODEL}', // Options: 'auto', 'sonnet-4.6', 'opus-4.6', 'sonnet-4.5', 'opus-4.5'
459
- maxTokens: 4096,
460
- },
461
-
462
- // OR use Cursor Agent (comment out claude block above, uncomment this):
463
- // cursor: {
464
- // model: 'auto', // Options: 'auto', 'opus-4.5', 'opus-4.5-thinking', 'sonnet-4.5', 'sonnet-4.5-thinking', 'composer-1', 'gpt-5.2-codex', 'gpt-5.2', 'gemini-3-pro', 'gemini-3-flash'
465
- // },` : `
466
- // Cursor Agent settings
467
- cursor: {
468
- model: 'auto', // Options: 'auto', 'opus-4.5', 'opus-4.5-thinking', 'sonnet-4.5', 'sonnet-4.5-thinking', 'composer-1', 'gpt-5.2-codex', 'gpt-5.2', 'gemini-3-pro', 'gemini-3-flash'
469
- },
470
-
471
- // OR use Claude (comment out cursor block above, uncomment this):
472
- // claude: {
473
- // model: '${DEFAULT_CLAUDE_MODEL}', // Options: 'auto', 'sonnet-4.6', 'opus-4.6', 'sonnet-4.5', 'opus-4.5'
474
- // maxTokens: 4096,
475
- // },`}
476
- strictMode: false, // Set to true if you need reliable structured output enforcement for production workflows (requires authentication)
477
- },
478
-
479
- // Advanced: Override models per node (optional)
480
- // models: {
481
- // default: 'auto', // Fallback for all nodes
482
- // execute_live: 'claude-opus-4', // Override specific node
483
- // },
484
-
485
- // Folder paths
486
- paths: {
487
- specs: 'test-specs', // Where your .txt test specs are
488
- generated: 'tests', // Where generated .spec.js files go
489
- output: '.zibby/output', // Where workflow execution results are saved (default: .zibby/output)
490
- // sessionPrefix: 'run', // Optional: prefix for session folders (e.g., run_1772788458045)
491
- },
492
-
493
- // Context discovery - auto-discovers CONTEXT.md & AGENTS.md files (cascades from root → spec directory)
494
- // Override filenames to search for different files
495
- context: {
496
- filenames: ['CONTEXT.md', 'AGENTS.md'], // Auto-discover these files (walks up from spec directory)
497
- discovery: {
498
- env: \`env-\${process.env.ENV || 'local'}.js\`, // Additional explicit files
499
- // fixtures: 'fixtures.js', // Add more as needed
500
- }
501
- },
502
-
503
- // Video recording (affects playwright.config.js generation)
504
- video: 'on', // Options: 'off', 'on', 'retain-on-failure', 'on-first-retry'
505
-
506
- // Browser viewport size for test execution
507
- // Default: 1280x720 (works well on most screens)
508
- // For larger displays: { width: 1920, height: 1080 }
509
- // For mobile testing: { width: 375, height: 667 } (iPhone SE)
510
- viewport: { width: 1280, height: 720 },
511
-
512
- // Playwright artifacts (screenshots, traces, videos)
513
- // Traces contain EXACT selectors for 100% accurate script generation
514
- // Default: true (stored in test-results/playwright, separate from workflow output)
515
- playwrightArtifacts: true,
516
-
517
- // Cloud sync - auto-upload test results & videos (requires ZIBBY_API_KEY in .env)
518
- cloudSync: ${answers.cloudSync || false}
519
- };
520
- `;
521
- }
522
-
523
- function generateEnvFile(answers) {
524
- return `# Zibby Test Automation - Environment Variables
525
-
526
- # AI Provider Keys
527
- ${answers.agent === 'claude' ? '# Claude (Anthropic) - Direct API\nANTHROPIC_API_KEY=sk-ant-your_key_here' : '# ANTHROPIC_API_KEY=sk-ant-your_key_here'}
528
- ${answers.agent === 'cursor' ? '# Cursor Agent (uses cursor-agent CLI)\n# No API key needed if cursor-agent is installed\n# Run: curl https://cursor.com/install -fsS | bash' : ''}
529
-
530
- # Zibby Cloud Sync (for uploading test results & videos)
531
- # Get your API key from: https://zibby.app/settings/tokens
532
- # ZIBBY_API_KEY=zby_your_api_key_here
533
-
534
- # Test Memory (Dolt DB) - Auto-compaction settings
535
- # ZIBBY_MEMORY_MAX_RUNS=3000 # Max test runs to keep per spec
536
- # ZIBBY_MEMORY_MAX_AGE=1095 # Max age in days for stale data (~3 years)
537
- # ZIBBY_MEMORY_COMPACT_EVERY=1500 # Auto-compact every N runs (0 to disable)
538
- `;
539
- }
540
-
541
- function generateEnvFileWithKey(answers, apiKey) {
542
- return `# Zibby Test Automation - Environment Variables
543
-
544
- # AI Provider Keys
545
- ${answers.agent === 'claude' ? 'ANTHROPIC_API_KEY=sk-ant-your_key_here' : '# ANTHROPIC_API_KEY=sk-ant-your_key_here'}
546
-
547
- # Zibby Cloud Sync
548
- ZIBBY_API_KEY=${apiKey}
549
-
550
- # Test Memory (Dolt DB) - Auto-compaction settings
551
- # ZIBBY_MEMORY_MAX_RUNS=3000 # Max test runs to keep per spec
552
- # ZIBBY_MEMORY_MAX_AGE=1095 # Max age in days for stale data (~3 years)
553
- # ZIBBY_MEMORY_COMPACT_EVERY=1500 # Auto-compact every N runs (0 to disable)
554
- `;
555
- }
556
-
557
- function generatePackageJson(projectName, _answers) {
558
- return JSON.stringify({
559
- name: projectName,
560
- version: '1.0.0',
561
- type: 'module',
562
- private: true,
563
- scripts: {
564
- 'test:spec': 'zibby run',
565
- 'test': 'playwright test',
566
- 'test:headed': 'playwright test --headed'
567
- },
568
- dependencies: {
569
- '@zibby/cli': '^0.1.25',
570
- '@zibby/core': '^0.1.20'
571
- },
572
- devDependencies: {
573
- '@playwright/test': '^1.49.0',
574
- 'dotenv': '^17.2.3'
575
- }
576
- }, null, 2);
577
- }
578
-
579
- function generateGitignore() {
580
- return `# Dependencies
581
- node_modules/
582
-
583
- # Test artifacts
584
- .zibby/output/
585
- playwright-report/
586
- tests/
587
- *.webm
588
- *.mp4
589
-
590
- # Environment variables
591
- .env
592
-
593
- # OS
594
- .DS_Store
595
- Thumbs.db
596
-
597
- # IDE
598
- .vscode/
599
- .idea/
600
-
601
- # Zibby cache
602
- .zibby/cache/
603
- .zibby/tenancy.json
604
-
605
- # Zibby memory (Dolt DB synced separately via dolt remote, not git)
606
- .zibby/memory/
607
- .zibby/memory-context.md
608
- `;
609
- }
610
-
611
- function generatePlaywrightConfig(_videoOption = 'off', outputDir = null) {
612
- const outputDirLine = outputDir ? ` outputDir: '${outputDir}',\n` : ` outputDir: 'test-results/playwright', // Keep Playwright artifacts separate from Zibby workflow output\n`;
613
-
614
- return `import { defineConfig} from '@playwright/test';
615
-
616
- export default defineConfig({
617
- testDir: './tests',
618
- ${outputDirLine} timeout: 30000,
619
- retries: 0,
620
- workers: 1,
621
-
622
- use: {
623
- headless: process.env.PLAYWRIGHT_HEADLESS === '1' ? true : false,
624
- viewport: { width: 1280, height: 720 },
625
- screenshot: 'off',
626
- video: 'off',
627
- },
628
-
629
- reporter: [
630
- ['html'],
631
- ['list']
632
- ],
633
- });
634
- `;
635
- }
636
-
637
- function generateExampleSpec() {
638
- return `Test Specification: Example Domain Navigation
639
- ==============================================
640
-
641
- Application: Example Domain
642
- URL: https://example.com
643
- Feature: Basic navigation and link verification
644
-
645
- Test Objective:
646
- ---------------
647
- Verify that the example.com website loads correctly, displays expected content,
648
- and navigational links function as intended.
649
-
650
- Test Steps:
651
- -----------
652
- 1. Navigate to https://example.com
653
- 2. Verify the page title contains "Example Domain"
654
- 3. Verify the main heading is visible and contains "Example Domain"
655
- 4. Locate and verify the "More information..." link is present
656
- 5. Click the "More information..." link
657
- 6. Verify navigation to IANA domain information page
658
- 7. Verify the new page URL contains "iana.org"
659
- 8. Verify the page loaded successfully (no errors)
660
-
661
- Expected Results:
662
- -----------------
663
- - Initial page loads within 3 seconds
664
- - "Example Domain" heading is clearly visible
665
- - "More information..." link is clickable and visible
666
- - Navigation to IANA page succeeds
667
- - No console errors or network failures
668
- - Page title updates after navigation
669
-
670
- Notes:
671
- ------
672
- - Uses a stable, public domain (example.com) maintained by IANA
673
- - No authentication required
674
- - Suitable for CI/CD smoke testing
675
- `;
676
- }
677
-
678
- function generateReadme(projectName, answers) {
679
- return `# ${projectName}
680
-
681
- AI-powered test automation with Zibby.
682
-
683
- ## Setup
684
-
685
- 1. Install dependencies:
686
- \`\`\`bash
687
- npm install
688
- \`\`\`
689
-
690
- 2. Configure environment:
691
- \`\`\`bash
692
- cp .env.example .env
693
- # Edit .env and add your ${answers.agent === 'claude' ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY'}
694
- \`\`\`
695
-
696
- 3. Run example test:
697
- \`\`\`bash
698
- npx zibby run test-specs/examples/example-domain.txt
699
- \`\`\`
700
-
701
- ## Usage
702
-
703
- ### Run Test Specs
704
-
705
- \`\`\`bash
706
- # Run a test spec (executes + generates + verifies)
707
- npx zibby run test-specs/auth/login.txt
708
-
709
- # Run in headless mode
710
- npx zibby run test-specs/auth/login.txt --headless
711
- \`\`\`
712
-
713
- ### Write Test Specs
714
-
715
- Create test specifications in \`test-specs/\`:
716
-
717
- \`\`\`
718
- test-specs/
719
- auth/
720
- login.txt
721
- logout.txt
722
- dashboard/
723
- overview.txt
724
- \`\`\`
725
-
726
- ### Run Generated Tests
727
-
728
- \`\`\`bash
729
- # Run all generated tests
730
- npx playwright test
731
-
732
- # Run specific test
733
- npx playwright test tests/auth/login.spec.js
734
-
735
- # Run with UI
736
- npx playwright test --ui
737
- \`\`\`
738
-
739
- ## Configuration
740
-
741
- Edit \`.zibby.config.mjs\` to customize:
742
- - Agent settings (model, temperature)
743
- - Browser settings (headless, viewport)
744
- - Cloud sync${answers.cloudSync ? ' (enabled)' : ' (disabled)'}
745
- - Self-healing behavior
746
-
747
- ## Project Structure
748
-
749
- \`\`\`
750
- ${projectName}/
751
- ├── .zibby/
752
- │ ├── graph.mjs # Workflow definition
753
- │ ├── nodes/ # Custom nodes
754
- │ └── output/ # Workflow execution results (gitignored)
755
- │ └── sessions/ # Session artifacts & recordings
756
- ├── .zibby.config.mjs # Configuration
757
- ├── .env.example # Environment template (cp to .env and configure)
758
- ├── test-specs/ # Test specifications (committed)
759
- │ └── examples/
760
- │ └── example-domain.txt
761
- └── tests/ # Generated tests (gitignored)
762
- \`\`\`
763
-
764
- ## Learn More
765
-
766
- - Documentation: https://docs.zibby.dev
767
- - Examples: https://github.com/zibby/examples
768
- `;
769
- }
770
-