@zibby/core 0.4.6 → 0.5.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.
Files changed (78) hide show
  1. package/dist/index.js +150 -153
  2. package/dist/package.json +2 -9
  3. package/dist/utils/run-index-post-cli.js +1 -4
  4. package/package.json +2 -9
  5. package/dist/templates/browser-test-automation/README.md +0 -136
  6. package/dist/templates/browser-test-automation/chat.mjs +0 -36
  7. package/dist/templates/browser-test-automation/graph.mjs +0 -80
  8. package/dist/templates/browser-test-automation/nodes/cache-replay.mjs +0 -213
  9. package/dist/templates/browser-test-automation/nodes/execute-live.mjs +0 -254
  10. package/dist/templates/browser-test-automation/nodes/generate-script.mjs +0 -108
  11. package/dist/templates/browser-test-automation/nodes/index.mjs +0 -4
  12. package/dist/templates/browser-test-automation/nodes/preflight.mjs +0 -94
  13. package/dist/templates/browser-test-automation/nodes/utils.mjs +0 -297
  14. package/dist/templates/browser-test-automation/pipeline-ids.js +0 -12
  15. package/dist/templates/browser-test-automation/result-handler.mjs +0 -327
  16. package/dist/templates/browser-test-automation/run-index.mjs +0 -420
  17. package/dist/templates/browser-test-automation/run_test.json +0 -358
  18. package/dist/templates/browser-test-automation/state.js +0 -61
  19. package/dist/templates/code-analysis/README.md +0 -60
  20. package/dist/templates/code-analysis/graph.js +0 -72
  21. package/dist/templates/code-analysis/graph.mjs +0 -33
  22. package/dist/templates/code-analysis/index.js +0 -18
  23. package/dist/templates/code-analysis/nodes/analyze-ticket-node.js +0 -204
  24. package/dist/templates/code-analysis/nodes/create-pr-node.js +0 -175
  25. package/dist/templates/code-analysis/nodes/finalize-node.js +0 -118
  26. package/dist/templates/code-analysis/nodes/generate-code-node.js +0 -425
  27. package/dist/templates/code-analysis/nodes/generate-test-cases-node.js +0 -376
  28. package/dist/templates/code-analysis/nodes/services/prMetaService.js +0 -86
  29. package/dist/templates/code-analysis/nodes/setup-node.js +0 -142
  30. package/dist/templates/code-analysis/prompts/analyze-ticket.md +0 -181
  31. package/dist/templates/code-analysis/prompts/generate-code.md +0 -33
  32. package/dist/templates/code-analysis/prompts/generate-test-cases.md +0 -110
  33. package/dist/templates/code-analysis/state.js +0 -48
  34. package/dist/templates/generate-test-cases/README.md +0 -72
  35. package/dist/templates/generate-test-cases/graph.mjs +0 -46
  36. package/dist/templates/generate-test-cases/nodes/generate-test-cases-node.js +0 -381
  37. package/dist/templates/generate-test-cases/nodes/setup-node.js +0 -142
  38. package/dist/templates/generate-test-cases/state.js +0 -54
  39. package/dist/templates/global-setup.js +0 -56
  40. package/dist/templates/index.js +0 -147
  41. package/dist/templates/register-nodes.js +0 -24
  42. package/templates/browser-test-automation/README.md +0 -136
  43. package/templates/browser-test-automation/chat.mjs +0 -36
  44. package/templates/browser-test-automation/graph.mjs +0 -80
  45. package/templates/browser-test-automation/nodes/cache-replay.mjs +0 -213
  46. package/templates/browser-test-automation/nodes/execute-live.mjs +0 -254
  47. package/templates/browser-test-automation/nodes/generate-script.mjs +0 -108
  48. package/templates/browser-test-automation/nodes/index.mjs +0 -4
  49. package/templates/browser-test-automation/nodes/preflight.mjs +0 -94
  50. package/templates/browser-test-automation/nodes/utils.mjs +0 -297
  51. package/templates/browser-test-automation/pipeline-ids.js +0 -12
  52. package/templates/browser-test-automation/result-handler.mjs +0 -327
  53. package/templates/browser-test-automation/run-index.mjs +0 -420
  54. package/templates/browser-test-automation/run_test.json +0 -358
  55. package/templates/browser-test-automation/state.js +0 -61
  56. package/templates/code-analysis/README.md +0 -60
  57. package/templates/code-analysis/graph.js +0 -72
  58. package/templates/code-analysis/graph.mjs +0 -33
  59. package/templates/code-analysis/index.js +0 -18
  60. package/templates/code-analysis/nodes/analyze-ticket-node.js +0 -204
  61. package/templates/code-analysis/nodes/create-pr-node.js +0 -175
  62. package/templates/code-analysis/nodes/finalize-node.js +0 -118
  63. package/templates/code-analysis/nodes/generate-code-node.js +0 -425
  64. package/templates/code-analysis/nodes/generate-test-cases-node.js +0 -376
  65. package/templates/code-analysis/nodes/services/prMetaService.js +0 -86
  66. package/templates/code-analysis/nodes/setup-node.js +0 -142
  67. package/templates/code-analysis/prompts/analyze-ticket.md +0 -181
  68. package/templates/code-analysis/prompts/generate-code.md +0 -33
  69. package/templates/code-analysis/prompts/generate-test-cases.md +0 -110
  70. package/templates/code-analysis/state.js +0 -48
  71. package/templates/generate-test-cases/README.md +0 -72
  72. package/templates/generate-test-cases/graph.mjs +0 -46
  73. package/templates/generate-test-cases/nodes/generate-test-cases-node.js +0 -381
  74. package/templates/generate-test-cases/nodes/setup-node.js +0 -142
  75. package/templates/generate-test-cases/state.js +0 -54
  76. package/templates/global-setup.js +0 -56
  77. package/templates/index.js +0 -147
  78. package/templates/register-nodes.js +0 -24
@@ -1,381 +0,0 @@
1
- /**
2
- * Generate Test Cases Node - Generate human-readable test case specifications
3
- * Used by: analysisGraph, implementationGraph
4
- *
5
- * Output: Plain-text test specifications for AI interpretation
6
- * - High-level test steps in natural language
7
- * - Includes credentials, prerequisites, and expected results
8
- * - Can be executed by AI agents (like MCP) and converted to Playwright
9
- */
10
-
11
- import { invokeAgent } from '@zibby/core';
12
- import { z } from 'zod';
13
- import { randomBytes } from 'crypto';
14
- import { adfToText } from '@zibby/core/utils/adf-converter.js';
15
-
16
- // Generate a simple unique ID
17
- const generateId = () => randomBytes(8).toString('hex');
18
-
19
- // Schema for plain-text test specification
20
- const TestSpecificationSchema = z.object({
21
- id: z.string().optional(),
22
- title: z.string().describe('Test name (e.g., "Login Functionality", "Create New User")'),
23
- application: z.string().describe('Application name being tested'),
24
- url: z.string().describe('Base URL of the application'),
25
- testerRole: z.string().describe('Role performing the test (e.g., "Admin user", "End user")'),
26
- feature: z.string().describe('Feature being tested (e.g., "Authentication - Login")'),
27
- ticketId: z.string().describe('Associated ticket ID'),
28
- testCredentials: z.array(z.object({
29
- field: z.string().describe('Credential field name (e.g., "Email", "Password", "API Key")'),
30
- value: z.string().describe('Credential value')
31
- })).optional().describe('Test credentials needed (if any)'),
32
- testObjective: z.string().describe('One clear sentence: what this test verifies'),
33
- testSteps: z.array(z.string()).describe('Plain English steps - natural language instructions (e.g., "Navigate to /login", "Enter email: test@example.com", "Click the submit button")'),
34
- expectedResults: z.array(z.string()).describe('What should happen - bullet points of expected outcomes'),
35
- status: z.string().default('Ready for automation').describe('Test status'),
36
- priority: z.enum(['Critical', 'High', 'Medium', 'Low']).describe('Test priority'),
37
- category: z.string().describe('Test category (e.g., "Smoke Test", "Regression", "Integration")')
38
- });
39
-
40
- const CategoryCountSchema = z.object({
41
- category: z.string().describe('Category name'),
42
- count: z.number().describe('Number of tests in this category')
43
- });
44
-
45
- // Schema for AI agent output (what invokeAgent validates)
46
- const TestSuiteSchema = z.object({
47
- testCases: z.array(TestSpecificationSchema).describe('Array of test specifications'),
48
- summary: z.object({
49
- totalTests: z.number(),
50
- byCategory: z.array(CategoryCountSchema).optional(),
51
- coverageNotes: z.string().optional()
52
- }).optional()
53
- });
54
-
55
- // Node output schema (what this node returns to state)
56
- const GenerateTestCasesOutputSchema = z.object({
57
- success: z.boolean(),
58
- tests: z.object({
59
- structured: z.object({
60
- testCases: z.array(z.any()),
61
- summary: z.object({
62
- totalTests: z.number(),
63
- byCategory: z.array(CategoryCountSchema).optional()
64
- }).optional()
65
- }),
66
- timestamp: z.string()
67
- }).nullable()
68
- });
69
-
70
- export const generateTestCasesNode = {
71
- name: 'generate_test_cases',
72
-
73
- // Output schema defines what this node returns to state
74
- outputSchema: GenerateTestCasesOutputSchema,
75
-
76
- execute: async (state) => {
77
- console.log('\n🧪 Generating test cases...');
78
-
79
- const { ticketContext, workspace, model, nodeConfigs = {} } = state;
80
- const aiModel = model || ticketContext.model || 'auto';
81
- // Standalone-template variant: this template runs WITHOUT an upstream
82
- // generate_code node, so the diff comes in directly as
83
- // `state.codeImplementation`. Keep the original two fallbacks so the
84
- // node still works if someone wires it after a code-gen node.
85
- const codeImpl = state.codeImplementation ||
86
- state.generate_code?.codeImplementation ||
87
- state.implement_code?.codeImplementation;
88
-
89
- if (!codeImpl || !codeImpl.diff) {
90
- console.log('⚠️ No code changes detected, skipping test generation');
91
- return {
92
- success: true,
93
- tests: null
94
- };
95
- }
96
-
97
- // Get node config for this node (if user configured it)
98
- const nodeConfig = nodeConfigs.generate_test_cases || {};
99
- console.log('📋 Node config:', JSON.stringify(nodeConfig, null, 2));
100
-
101
- // Build test generation prompt with workspace path for codebase exploration
102
- const testPrompt = buildTestGenerationPrompt(ticketContext, codeImpl, workspace, nodeConfig);
103
-
104
- console.log(`🚀 Running AI Agent to generate tests with model: ${aiModel}...`);
105
-
106
- const agentResult = await invokeAgent(testPrompt, {
107
- state,
108
- model: aiModel,
109
- schema: TestSuiteSchema
110
- });
111
-
112
- const validatedOutput = agentResult?.structured || agentResult;
113
-
114
- // Convert structured AI output → simple { id, title, priority, content } format
115
- const testCases = (validatedOutput?.testCases || []).map(test => ({
116
- id: generateId(),
117
- title: test.title,
118
- priority: test.priority || 'Medium',
119
- content: renderTestCaseToText(test),
120
- status: 'pending'
121
- }));
122
-
123
- console.log(`✅ Generated ${testCases.length} test cases`);
124
-
125
- return {
126
- success: true,
127
- tests: {
128
- structured: {
129
- testCases,
130
- summary: validatedOutput.summary || {
131
- totalTests: testCases.length,
132
- byCategory: Object.entries(
133
- testCases.reduce((acc, tc) => {
134
- acc[tc.category] = (acc[tc.category] || 0) + 1;
135
- return acc;
136
- }, {})
137
- ).map(([category, count]) => ({ category, count }))
138
- }
139
- },
140
- timestamp: new Date().toISOString()
141
- }
142
- };
143
- }
144
- };
145
-
146
- function renderTestCaseToText(test) {
147
- const lines = [];
148
-
149
- if (test.testObjective) {
150
- lines.push('WHAT THIS TESTS');
151
- lines.push(test.testObjective);
152
- lines.push('');
153
- }
154
-
155
- if (test.url) {
156
- lines.push('START URL');
157
- lines.push(test.url);
158
- lines.push('');
159
- }
160
-
161
- if (test.testCredentials && test.testCredentials.length > 0) {
162
- lines.push('TEST CREDENTIALS');
163
- test.testCredentials.forEach(cred => {
164
- lines.push(`• ${cred.field}: ${cred.value}`);
165
- });
166
- lines.push('');
167
- }
168
-
169
- if (test.testSteps && test.testSteps.length > 0) {
170
- lines.push('TEST STEPS');
171
- test.testSteps.forEach((step, i) => {
172
- lines.push(`${i + 1}. ${step}`);
173
- });
174
- lines.push('');
175
- }
176
-
177
- if (test.expectedResults && test.expectedResults.length > 0) {
178
- lines.push('EXPECTED RESULTS');
179
- test.expectedResults.forEach(result => {
180
- lines.push(`• ${result}`);
181
- });
182
- }
183
-
184
- return lines.join('\n').trim();
185
- }
186
-
187
- function buildTestGenerationPrompt(ticketContext, codeImpl, workspace, nodeConfig = {}) {
188
- // Extract user-provided context (test URL, admin credentials, etc.)
189
- const extractContext = nodeConfig.extractContext || {};
190
- let contextSection = '';
191
- if (extractContext.testBaseUrl || extractContext.adminUsername || extractContext.testAccountUsername) {
192
- contextSection = `
193
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
194
- PROVIDED TEST CONTEXT (USE THESE IN TEST CASES)
195
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
196
-
197
- ${extractContext.testBaseUrl ? `Test Base URL: ${extractContext.testBaseUrl}` : ''}
198
- ${extractContext.adminUsername ? `Admin Username: ${extractContext.adminUsername}` : ''}
199
- ${extractContext.adminPassword ? `Admin Password: ${extractContext.adminPassword}` : ''}
200
- ${extractContext.testAccountUsername ? `Test Account Username: ${extractContext.testAccountUsername}` : ''}
201
- ${extractContext.testAccountPassword ? `Test Account Password: ${extractContext.testAccountPassword}` : ''}
202
-
203
- **IMPORTANT**: Use these exact values in your test cases. Do NOT make up URLs or credentials.
204
-
205
- `;
206
- }
207
-
208
- return `You are an AI test automation engineer creating human-readable test specifications.
209
-
210
- WORKSPACE: ${workspace}
211
-
212
- TICKET: ${ticketContext.ticketKey} - ${ticketContext.summary}
213
- ${ticketContext.description ? `Description: ${typeof ticketContext.description === 'object' ? adfToText(ticketContext.description) : ticketContext.description}` : ''}
214
-
215
- MODIFIED FILES:
216
- ${codeImpl.changedFiles.map(f => ` - ${f}`).join('\n')}
217
-
218
- FULL CODE DIFF:
219
- \`\`\`diff
220
- ${codeImpl.diff}
221
- \`\`\`
222
-
223
- ${contextSection}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
224
-
225
- YOUR JOB: Generate 4-8 test specifications in plain-text format that AI agents can interpret.
226
-
227
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
228
- STEP 1: EXPLORE THE CODEBASE (MANDATORY - DO NOT SKIP)
229
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
230
-
231
- YOU MUST explore the codebase to understand the application structure BEFORE writing tests.
232
-
233
- REQUIRED EXPLORATION:
234
-
235
- 1. **Find and Read Routing Files**
236
- - Search the workspace for routing files: look for common names like App, routes, router, main, index files in .js/.jsx/.ts/.tsx format
237
- - Check common locations: src/, app/, client/, web/, components/, pages/ directories
238
- - Identify ALL routes and their URL patterns
239
- - Pay special attention to routes with parameters (e.g., /stores/:storeId/products, /users/:userId/orders, /items/:itemId)
240
-
241
- 2. **Understand Route Parameters**
242
- - Dynamic segments in routes (marked with :paramName or {paramName}) are PLACEHOLDERS
243
- - You MUST include navigation steps to select/create that resource first
244
- - Example: To reach /stores/:storeId/products, you must first navigate to /stores list and select a specific store
245
- - Example: To reach /orders/:orderId/details, you must first navigate to /orders list and click on a specific order
246
-
247
- 3. **Map Navigation Flow**
248
- - Understand the hierarchy: authentication → landing/home → resource lists → individual resources → sub-features
249
- - Find what links, buttons, tabs exist on each page to navigate to the next level
250
- - Read page components to understand the UI structure and navigation elements
251
-
252
- 4. **Find Authentication**
253
- - Check for login/signin routes, auth components, protected routes, authentication guards
254
- - Look for HOCs, route wrappers, or middleware that handle authentication
255
- - Understand if authentication is required and what the login flow looks like
256
-
257
- 5. **Identify Page Components**
258
- - Find the actual page/view components referenced in routes
259
- - Read their code to understand imports, props, state management, and structure
260
- - Understand what data they load, what APIs they call, and what user actions they support
261
-
262
- CRITICAL RULES:
263
- - Routes with dynamic parameters (like :id, :userId, :orderId) REQUIRE parent resource selection
264
- - You CANNOT jump directly to a URL with an ID - you must navigate through the parent list and SELECT that item
265
- - Base your test steps on ACTUAL navigation paths you discover in the codebase
266
- - If you don't explore properly, your tests will have WRONG navigation and be unusable by AI agents
267
-
268
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
269
- STEP 2: GENERATE TEST SPECIFICATIONS
270
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
271
-
272
- For each test, provide:
273
- - **title**: Test name (e.g., "Filter Orders by Status")
274
- - **application**: Application name (infer from package.json or codebase)
275
- - **url**: Base application URL (usually http://localhost:3000 or staging URL if found)
276
- - **testerRole**: Who performs this test (e.g., "Admin user", "End user", "Guest")
277
- - **feature**: Feature being tested (match the modified files)
278
- - **ticketId**: Use the ticket key provided above
279
- - **testCredentials**: Array of credentials needed (if authentication required):
280
- - field: "Email" / "Password" / "Username" / "API Key", etc.
281
- - value: Use realistic test data (e.g., "admin@example.com", "TestPass123")
282
- - **testObjective**: One clear sentence describing what this test verifies
283
- - **testSteps**: Plain English steps - MUST match actual navigation flow you discovered. Example:
284
- * "Navigate to login page http://localhost:3000/login"
285
- * "Enter email: admin@example.com"
286
- * "Enter password: TestPass123"
287
- * "Click the Sign In button"
288
- * "Navigate to Projects page http://localhost:3000/projects"
289
- * "Click on project 'Test Project 1'"
290
- * "Navigate to Orders tab"
291
- * "Click the status filter dropdown"
292
- * "Select 'Completed' from the filter"
293
- * "Verify only completed orders are displayed"
294
- DON'T use technical terms like "action", "target", "selector" - just natural language
295
- - **expectedResults**: Bullet points of what should happen
296
- - **priority**: Critical, High, Medium, or Low
297
- - **category**: Test category (e.g., "Smoke Test", "Regression", "Integration")
298
-
299
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
300
- EXAMPLE: Multi-Level Navigation (Generic Restaurant Ordering App)
301
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
302
-
303
- Assume this routing structure (discovered from App.jsx):
304
- /login
305
- /restaurants
306
- /restaurants/:restaurantId/menu
307
- /restaurants/:restaurantId/menu/:itemId/details
308
-
309
- CORRECT Test Steps (follows nested structure):
310
- {
311
- "title": "Add Menu Item to Cart with Size Selection",
312
- "application": "Restaurant Ordering System",
313
- "url": "http://localhost:3000",
314
- "testerRole": "Customer",
315
- "feature": "Menu - Item Size Selection",
316
- "ticketId": "REST-456",
317
- "testCredentials": [
318
- { "field": "Email", "value": "customer@email.com" },
319
- { "field": "Password", "value": "Order123" }
320
- ],
321
- "testObjective": "Verify customers can select different sizes when adding menu items to cart",
322
- "testSteps": [
323
- "Navigate to login page http://localhost:3000/login",
324
- "Enter email: customer@email.com",
325
- "Enter password: Order123",
326
- "Click the Sign In button",
327
- "Navigate to Restaurants page http://localhost:3000/restaurants",
328
- "Click on restaurant 'Downtown Pizza'",
329
- "Wait for menu to load",
330
- "Scroll to 'Beverages' section",
331
- "Click on menu item 'Lemonade'",
332
- "Wait for item details to display",
333
- "Click the size dropdown",
334
- "Select 'Large' size option",
335
- "Verify price updates to large size price",
336
- "Click Add to Cart button",
337
- "Verify item added to cart with Large size"
338
- ],
339
- "expectedResults": [
340
- "Size dropdown displays all available sizes (Small, Medium, Large)",
341
- "Price updates when different size is selected",
342
- "Item is added to cart with correct size",
343
- "Cart icon shows updated item count",
344
- "No errors displayed during the process"
345
- ],
346
- "priority": "High",
347
- "category": "Smoke Test"
348
- }
349
-
350
- WRONG Test Steps (skips required navigation):
351
- ❌ "Navigate to Menu page" (Too vague - which restaurant?)
352
- ❌ "Go to /restaurants/123/menu" (Can't directly use IDs without selection)
353
- ❌ "Select item from menu" (Need to navigate to restaurant first)
354
-
355
- CORRECT Test Steps (shows proper navigation):
356
- ✅ "Navigate to Restaurants page http://localhost:3000/restaurants"
357
- ✅ "Click on restaurant 'Downtown Pizza'"
358
- ✅ "Wait for menu to load"
359
- ✅ "Click on menu item 'Lemonade'"
360
-
361
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
362
- FINAL REMINDERS
363
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
364
-
365
- 1. ALWAYS explore routing files FIRST
366
- 2. NEVER skip navigation steps - include full path from login to feature
367
- 3. If routes have :parameters, include parent resource selection
368
- 4. Use ACTUAL URLs and paths from the codebase
369
- 5. Write plain English steps - no technical jargon
370
- 6. Base tests on REAL navigation flow, not assumptions
371
-
372
- Now generate test specifications for the ticket above.`;
373
- }
374
- /**
375
- * Agent Strategy Handling:
376
- * - Claude: Uses tool calling with schema → guaranteed structured output
377
- * - Cursor: Post-processes raw output with LangChain withStructuredOutput
378
- *
379
- * Node receives validated structured data regardless of agent type.
380
- */
381
-
@@ -1,142 +0,0 @@
1
- /**
2
- * Setup Node - Clone repositories and initialize git baseline
3
- * Used by: analysisGraph, implementationGraph
4
- */
5
-
6
- import { spawn } from 'child_process';
7
- import { join } from 'path';
8
- import { z } from 'zod';
9
-
10
- const SetupOutputSchema = z.object({
11
- success: z.boolean(),
12
- clonedRepos: z.array(z.object({
13
- name: z.string(),
14
- path: z.string(),
15
- isPrimary: z.boolean().optional()
16
- })),
17
- baselineCommit: z.string()
18
- });
19
-
20
- export const setupNode = {
21
- name: 'setup',
22
- outputSchema: SetupOutputSchema,
23
- execute: async (state) => {
24
- console.log('\n🔧 Setting up environment...');
25
-
26
- const { workspace, repos, githubToken } = state;
27
- const gitlabToken = process.env.GITLAB_TOKEN || '';
28
- const gitlabUrl = process.env.GITLAB_URL || '';
29
-
30
- // DEBUG: Log token status
31
- console.log(`🔑 GitHub Token: ${githubToken ? 'Present' : 'MISSING'}`);
32
- console.log(`🔑 GitLab Token: ${gitlabToken ? 'Present' : 'MISSING'}`);
33
- if (gitlabUrl) console.log(`🔑 GitLab URL: ${gitlabUrl}`);
34
-
35
- // Log environment
36
- console.log('Container: ECS Fargate');
37
- console.log('Memory: 4GB');
38
- console.log('CPU: 2 vCPU');
39
- console.log('Tools: Node.js, Git, Cursor CLI, Zibby CLI');
40
- console.log(`Working directory: ${workspace}`);
41
-
42
- // Clone repositories
43
- console.log('\n📦 Cloning repositories...');
44
-
45
- const clonedRepos = [];
46
- for (const repo of repos) {
47
- console.log(`Cloning ${repo.name}...`);
48
-
49
- const repoDir = join(workspace, repo.name);
50
-
51
- // Use token for authentication based on provider
52
- let cloneUrl = repo.url;
53
- let cloneEnv = {};
54
- const isGitlab = repo.provider === 'gitlab' || (gitlabUrl && repo.url.includes(new URL(gitlabUrl).host));
55
- const isGithub = repo.provider === 'github' || repo.url.includes('github.com');
56
-
57
- if (isGithub && githubToken) {
58
- cloneUrl = repo.url.replace('https://github.com', `https://x-access-token:${githubToken}@github.com`);
59
- cloneEnv = { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_ASKPASS: 'echo' };
60
- } else if (isGitlab && gitlabToken && gitlabUrl) {
61
- try {
62
- const gitlabHost = new URL(gitlabUrl).host;
63
- cloneUrl = repo.url.replace(`https://${gitlabHost}`, `https://oauth2:${gitlabToken}@${gitlabHost}`);
64
- } catch (e) {
65
- console.warn(`⚠️ Failed to parse GITLAB_URL: ${e.message}`);
66
- }
67
- cloneEnv = { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_ASKPASS: 'echo' };
68
- }
69
-
70
- // Shallow clone with progress output (async, non-blocking)
71
- await execCommand(
72
- `git clone --progress --depth 1 --branch ${repo.branch} "${cloneUrl}" "${repoDir}"`,
73
- workspace,
74
- cloneEnv
75
- );
76
- console.log(`✓ Cloned ${repo.name} on branch ${repo.branch}`);
77
-
78
- clonedRepos.push({
79
- name: repo.name,
80
- path: repoDir,
81
- isPrimary: repo.isPrimary
82
- });
83
- }
84
-
85
- // Initialize git in workspace for diff tracking
86
- await execCommand('git init', workspace);
87
- await execCommand('git config user.email "zibby@agent.com"', workspace);
88
- await execCommand('git config user.name "Zibby Agent"', workspace);
89
- await execCommand('git add .', workspace);
90
- await execCommand('git commit --allow-empty -m "baseline"', workspace);
91
-
92
- console.log('✅ Environment ready');
93
-
94
- const baselineCommit = await execCommand('git rev-parse HEAD', workspace);
95
-
96
- return {
97
- success: true,
98
- clonedRepos,
99
- baselineCommit: baselineCommit.trim()
100
- };
101
- }
102
- };
103
-
104
- // Async version using spawn - streams output in real-time, doesn't block event loop
105
- async function execCommand(command, cwd, env = {}) {
106
- return new Promise((resolve, reject) => {
107
- const proc = spawn(command, {
108
- cwd,
109
- shell: true,
110
- env: Object.keys(env).length > 0 ? env : process.env
111
- });
112
-
113
- let stdout = '';
114
- let stderr = '';
115
-
116
- // Stream stdout as it comes (triggers middleware setInterval!)
117
- proc.stdout.on('data', (data) => {
118
- const output = data.toString();
119
- stdout += output;
120
- console.log(output.trimEnd());
121
- });
122
-
123
- // Stream stderr as it comes
124
- proc.stderr.on('data', (data) => {
125
- const output = data.toString();
126
- stderr += output;
127
- console.log(output.trimEnd());
128
- });
129
-
130
- proc.on('close', (code) => {
131
- if (code !== 0) {
132
- reject(new Error(`Command failed with exit code ${code}: ${command}`));
133
- } else {
134
- resolve(stdout || stderr || '');
135
- }
136
- });
137
-
138
- proc.on('error', (err) => {
139
- reject(new Error(`Command error: ${command} - ${err.message}`));
140
- });
141
- });
142
- }
@@ -1,54 +0,0 @@
1
- /**
2
- * State schema for the generate-test-cases standalone template.
3
- *
4
- * Same shape as code-analysis (workspace + repos + ticketContext) PLUS
5
- * a `codeImplementation` field — the diff this template generates tests
6
- * for. In code-analysis that field is produced by the upstream
7
- * generate_code node; here, the user provides it directly.
8
- */
9
-
10
- import { z } from 'zod';
11
-
12
- export const generateTestCasesStateSchema = z.object({
13
- workspace: z.string().describe('Local workspace path'),
14
-
15
- repos: z.array(z.object({
16
- name: z.string(),
17
- url: z.string().url(),
18
- path: z.string().optional(),
19
- branch: z.string().default('main'),
20
- isPrimary: z.boolean().default(false),
21
- })).optional().describe('Repository configurations (cloned by setup node so the LLM can explore routing/components)'),
22
-
23
- ticketContext: z.object({
24
- key: z.string().regex(/^[A-Z]+-\d+$/, 'Invalid ticket format (expected PROJ-123)').optional(),
25
- ticketKey: z.string().optional(),
26
- summary: z.string().min(1).describe('Ticket summary/title'),
27
- description: z.any().optional().describe('Ticket description (string or ADF object)'),
28
- acceptanceCriteria: z.string().optional(),
29
- type: z.string().optional(),
30
- priority: z.string().optional(),
31
- labels: z.array(z.string()).optional(),
32
- components: z.array(z.string()).optional(),
33
- }).describe('Jira/ticket context — informs test priorities + naming'),
34
-
35
- // The new direct-input field that distinguishes this standalone template
36
- // from code-analysis. In code-analysis this comes from generate_code's
37
- // output; here the user supplies it (e.g. from `git diff` of a PR they
38
- // want tests for).
39
- codeImplementation: z.object({
40
- diff: z.string().describe('Unified-diff string of the changes'),
41
- changedFiles: z.array(z.string()).describe('List of file paths touched'),
42
- }).describe('Code changes to generate tests for'),
43
-
44
- githubToken: z.string().optional().describe('GitHub PAT (needed only if repos[].url requires auth)'),
45
- model: z.string().default('auto').describe('AI model to use'),
46
- nodeConfigs: z.record(z.string(), z.any()).optional().describe('Per-node configuration overrides (e.g. extractContext for test credentials)'),
47
- });
48
-
49
- // Clean isolation: this schema declares ONLY what the template's nodes
50
- // actually need. No EXECUTION_ID / PROGRESS_QUEUE_URL / SQS_AUTH_TOKEN
51
- // / PROJECT_API_TOKEN — those were legacy analysis-UI plumbing fields
52
- // and the new templates run via the standard `workflow run` /
53
- // `workflow trigger` cloud pipeline, which has its own progress
54
- // reporting outside the state object.
@@ -1,56 +0,0 @@
1
- export default async function globalSetup() {
2
- console.log('🎭 Zibby global setup - enabling visual feedback...');
3
- }
4
-
5
- export async function setupRippleEffect(page) {
6
- await page.addInitScript(`
7
- const style = document.createElement('style');
8
- style.textContent = \`
9
- @keyframes zibby-ripple {
10
- 0% {
11
- transform: scale(0);
12
- opacity: 0.7;
13
- }
14
- 100% {
15
- transform: scale(4);
16
- opacity: 0;
17
- }
18
- }
19
- .zibby-ripple {
20
- position: absolute;
21
- border-radius: 50%;
22
- background: rgba(59, 130, 246, 0.7);
23
- pointer-events: none;
24
- animation: zibby-ripple 0.6s ease-out;
25
- z-index: 999999;
26
- }
27
- \`;
28
-
29
- document.addEventListener('DOMContentLoaded', () => {
30
- if (document.head && !document.getElementById('zibby-ripple-style')) {
31
- style.id = 'zibby-ripple-style';
32
- document.head.appendChild(style);
33
- }
34
- });
35
-
36
- if (document.head && !document.getElementById('zibby-ripple-style')) {
37
- style.id = 'zibby-ripple-style';
38
- document.head.appendChild(style);
39
- }
40
-
41
- window.__zibbyShowRipple = function(x, y) {
42
- const ripple = document.createElement('div');
43
- ripple.className = 'zibby-ripple';
44
- ripple.style.left = (x - 10) + 'px';
45
- ripple.style.top = (y - 10) + 'px';
46
- ripple.style.width = '20px';
47
- ripple.style.height = '20px';
48
-
49
- if (document.body) {
50
- document.body.appendChild(ripple);
51
- setTimeout(() => ripple.remove(), 600);
52
- }
53
- };
54
- `);
55
- }
56
-