@zibby/core 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/core",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Core test automation engine with multi-agent and multi-MCP support",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -32,19 +32,31 @@ export class CursorAgentStrategy extends AgentStrategy {
32
32
  for (const path of paths) {
33
33
  try {
34
34
  if (path.startsWith('/')) {
35
+ // Check file exists and is executable
35
36
  accessSync(path, constants.X_OK);
36
- logger.debug(`[Cursor] Found agent at: ${path}`);
37
- return true;
37
+ // Verify it actually runs
38
+ const out = execSync(`"${path}" --version 2>&1`, { encoding: 'utf-8', timeout: 3000, stdio: 'pipe' });
39
+ if (out && out.length > 0) {
40
+ logger.debug(`[Cursor] Found agent at: ${path} (version: ${out.trim().slice(0, 50)})`);
41
+ return true;
42
+ }
43
+ } else {
44
+ // Check if in PATH and runs
45
+ const which = execSync(`which ${path}`, { encoding: 'utf-8', timeout: 2000, stdio: 'pipe' }).trim();
46
+ if (!which) continue;
47
+ const out = execSync(`${path} --version 2>&1`, { encoding: 'utf-8', timeout: 3000, stdio: 'pipe' });
48
+ if (out && out.length > 0) {
49
+ logger.debug(`[Cursor] Found '${path}' in PATH at ${which} (version: ${out.trim().slice(0, 50)})`);
50
+ return true;
51
+ }
38
52
  }
39
- execSync(`${path} --version 2>/dev/null`, { stdio: 'ignore', timeout: 2000 });
40
- logger.debug(`[Cursor] Found '${path}' in PATH`);
41
- return true;
42
53
  } catch (_e) {
54
+ // Binary doesn't exist or doesn't work
43
55
  continue;
44
56
  }
45
57
  }
46
58
 
47
- logger.warn('[Cursor] ❌ Cursor Agent CLI not found');
59
+ logger.warn('[Cursor] ❌ Cursor Agent CLI not found or not working. Run: agent --version');
48
60
  return false;
49
61
  }
50
62
 
@@ -79,20 +91,34 @@ export class CursorAgentStrategy extends AgentStrategy {
79
91
  for (const bin of possibleBins) {
80
92
  try {
81
93
  if (bin.startsWith('/')) {
94
+ // For absolute paths, check file exists and is executable
82
95
  accessSync(bin, constants.X_OK);
96
+ // Also verify it actually runs
97
+ execSync(`"${bin}" --version 2>&1`, { encoding: 'utf-8', timeout: 3000, stdio: 'pipe' });
83
98
  } else {
84
- execSync(`which ${bin} 2>/dev/null`, { stdio: 'ignore', timeout: 2000 });
99
+ // For commands in PATH, verify they exist and run
100
+ const which = execSync(`which ${bin}`, { encoding: 'utf-8', timeout: 2000 }).trim();
101
+ if (!which) throw new Error('not in PATH');
102
+ // Verify the binary works
103
+ execSync(`${bin} --version 2>&1`, { encoding: 'utf-8', timeout: 3000, stdio: 'pipe' });
85
104
  }
86
105
  cursorBin = bin;
87
106
  logger.debug(`[Agent] Using binary: ${bin}`);
88
107
  break;
89
- } catch {
108
+ } catch (err) {
109
+ logger.debug(`[Agent] Binary '${bin}' check failed: ${err.message}`);
90
110
  continue;
91
111
  }
92
112
  }
93
113
 
94
114
  if (!cursorBin) {
95
- throw new Error('Cursor Agent CLI not found. Install it or add it to PATH.');
115
+ throw new Error(
116
+ 'Cursor Agent CLI not found or not working.\n\n' +
117
+ 'Install it with:\n' +
118
+ ' npm install -g @cursor/agent\n\n' +
119
+ 'Or set CURSOR_API_KEY and ensure "agent" is in your PATH.\n' +
120
+ 'Test with: agent --version'
121
+ );
96
122
  }
97
123
 
98
124
  // File-based structured output: agent writes JSON to this file
@@ -349,12 +375,16 @@ export class CursorAgentStrategy extends AgentStrategy {
349
375
 
350
376
  logger.debug(`💓 [Agent] Running for ${elapsed}s | ${lineCount} lines output${activity}`);
351
377
 
352
- if (lineCount === 0 && elapsed >= 90 && elapsed < 120 && modifiedFiles.size === 0) {
353
- logger.warn(`⚠️ [Agent] No output and no file changes after ${elapsed}s — agent may be stuck.`);
354
- try {
355
- const procInfo = execSync(`ps -p ${proc.pid} -o pid,stat,rss,time 2>/dev/null || echo "Process info unavailable"`, { encoding: 'utf-8', timeout: 5000 });
356
- logger.warn(`⚠️ [Agent] Process: ${procInfo.trim()}`);
357
- } catch {}
378
+ if (lineCount === 0 && elapsed >= 30 && modifiedFiles.size === 0) {
379
+ if (elapsed < 35) {
380
+ logger.warn(`⚠️ [Agent] No output after ${elapsed}s — agent may be stuck. Check your CURSOR_API_KEY.`);
381
+ }
382
+ if (elapsed >= 60) {
383
+ killed = true;
384
+ logger.error(`❌ [Agent] No response after ${elapsed}s — killing. Verify CURSOR_API_KEY is valid and agent CLI works: agent --version`);
385
+ proc.kill('SIGTERM');
386
+ setTimeout(() => { if (!proc.killed) proc.kill('SIGKILL'); }, 3000);
387
+ }
358
388
  }
359
389
  }, 30000);
360
390
 
package/src/index.js CHANGED
@@ -101,7 +101,8 @@ export async function runTest(specPath, config = {}) {
101
101
  const testSpec = readFileSync(specPath, 'utf-8');
102
102
 
103
103
  const adapter = null;
104
- let agent = await loadLocalAgent(cwd, agentConfig);
104
+ const { agent: localAgent, error: localAgentError } = await loadLocalAgent(cwd, agentConfig);
105
+ let agent = localAgent;
105
106
 
106
107
  if (!agent && config.fallbackAgentModule) {
107
108
  const mod = config.fallbackAgentModule;
@@ -111,11 +112,15 @@ export async function runTest(specPath, config = {}) {
111
112
  }
112
113
  }
113
114
 
115
+ if (!agent && localAgentError) {
116
+ console.warn(`⚠️ Failed to load local agent: ${localAgentError}`);
117
+ }
118
+
114
119
  if (!agent) {
115
120
  throw new Error(
116
121
  `No agent found. Please run:\n` +
117
122
  ` zibby init\n\n` +
118
- `This will create .zibby/graph.js with your workflow definition.`
123
+ `This will create .zibby/graph.mjs with your workflow definition.`
119
124
  );
120
125
  }
121
126
 
@@ -238,10 +243,10 @@ export async function listWorkflows(cwd = process.cwd()) {
238
243
  const { existsSync: fsExistsSync } = await import('fs');
239
244
  const { pathToFileURL } = await import('url');
240
245
 
241
- const localAgentPath = pathJoin(cwd, '.zibby/graph.js');
246
+ const localAgentPath = pathJoin(cwd, '.zibby/graph.mjs');
242
247
 
243
248
  if (!fsExistsSync(localAgentPath)) {
244
- return { available: [], default: null, error: 'No .zibby/graph.js found' };
249
+ return { available: [], default: null, error: 'No .zibby/graph.mjs found' };
245
250
  }
246
251
 
247
252
  const agentModule = await import(pathToFileURL(localAgentPath).href);
@@ -268,10 +273,10 @@ async function loadLocalAgent(cwd, config) {
268
273
  const { existsSync: fsExistsSync } = await import('fs');
269
274
  const { pathToFileURL } = await import('url');
270
275
 
271
- const localAgentPath = pathJoin(cwd, '.zibby/graph.js');
276
+ const localAgentPath = pathJoin(cwd, '.zibby/graph.mjs');
272
277
 
273
278
  if (!fsExistsSync(localAgentPath)) {
274
- return null;
279
+ return { agent: null, error: null };
275
280
  }
276
281
 
277
282
  const agentModule = await import(pathToFileURL(localAgentPath).href);
@@ -311,19 +316,17 @@ async function loadLocalAgent(cwd, config) {
311
316
  }
312
317
 
313
318
  if (!AgentClass) {
314
- console.warn('⚠️ Could not find any WorkflowAgent export in local graph.js');
315
- return null;
319
+ return { agent: null, error: 'Could not find any WorkflowAgent export in local graph.js' };
316
320
  }
317
321
 
318
322
  if (!AgentClass.name?.includes('auto-detected')) {
319
- console.log('✓ Using local agent from .zibby/graph.js');
323
+ console.log('✓ Using local agent from .zibby/graph.mjs');
320
324
  }
321
325
  }
322
326
 
323
- return new AgentClass(config);
327
+ return { agent: new AgentClass(config), error: null };
324
328
  } catch (error) {
325
- console.warn(`⚠️ Failed to load local agent: ${error.message}`);
326
- return null;
329
+ return { agent: null, error: error.message };
327
330
  }
328
331
  }
329
332
 
@@ -10,8 +10,8 @@ import {
10
10
  preflightNode,
11
11
  executeLiveNode,
12
12
  generateScriptNode,
13
- } from './nodes/index.js';
14
- import { BrowserTestResultHandler } from './result-handler.js';
13
+ } from './nodes/index.mjs';
14
+ import { BrowserTestResultHandler } from './result-handler.mjs';
15
15
 
16
16
  let memoryMiddleware = null;
17
17
  try {
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { z, SKILLS } from '@zibby/core';
13
- import { formatAssertionChecklist } from './utils.js';
13
+ import { formatAssertionChecklist } from './utils.mjs';
14
14
 
15
15
  export const executeLiveNode = {
16
16
  name: 'execute_live',
@@ -1,5 +1,5 @@
1
1
  import { z } from '@zibby/core';
2
- import { formatRecordedActions, formatAssertionsWithResults } from './utils.js';
2
+ import { formatRecordedActions, formatAssertionsWithResults } from './utils.mjs';
3
3
 
4
4
  const GenerateScriptOutputSchema = z.object({
5
5
  success: z.boolean(),
@@ -0,0 +1,3 @@
1
+ export { preflightNode } from './preflight.mjs';
2
+ export { executeLiveNode } from './execute-live.mjs';
3
+ export { generateScriptNode } from './generate-script.mjs';
@@ -41,7 +41,7 @@ export class TemplateFactory {
41
41
  }
42
42
 
43
43
  static validateTemplate(templatePath) {
44
- const requiredFiles = ['graph.js', 'nodes', 'README.md'];
44
+ const requiredFiles = ['graph.mjs', 'nodes', 'README.md'];
45
45
 
46
46
  for (const file of requiredFiles) {
47
47
  const filePath = join(templatePath, file);
@@ -57,9 +57,9 @@ export class TemplateFactory {
57
57
  const template = this.getTemplate(templateName);
58
58
  this.validateTemplate(template.path);
59
59
 
60
- const resultHandlerPath = join(template.path, 'result-handler.js');
60
+ const resultHandlerPath = join(template.path, 'result-handler.mjs');
61
61
  return {
62
- graphPath: join(template.path, 'graph.js'),
62
+ graphPath: join(template.path, 'graph.mjs'),
63
63
  nodesPath: join(template.path, 'nodes'),
64
64
  readmePath: join(template.path, 'README.md'),
65
65
  resultHandlerPath: existsSync(resultHandlerPath) ? resultHandlerPath : null,
@@ -1,3 +0,0 @@
1
- export { preflightNode } from './preflight.js';
2
- export { executeLiveNode } from './execute-live.js';
3
- export { generateScriptNode } from './generate-script.js';