@synergenius/flow-weaver 0.15.2 → 0.16.0

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.
@@ -191,8 +191,9 @@ export async function marketPackCommand(directory, options = {}) {
191
191
  validation,
192
192
  parsedFiles: parsedFiles.length,
193
193
  }, null, 2));
194
- if (!validation.valid)
195
- process.exit(1);
194
+ if (!validation.valid) {
195
+ process.exitCode = 1;
196
+ }
196
197
  return;
197
198
  }
198
199
  // Display results
@@ -277,7 +278,8 @@ export async function marketInstallCommand(packageSpec, options = {}) {
277
278
  else {
278
279
  logger.error(`npm install failed: ${getErrorMessage(err)}`);
279
280
  }
280
- process.exit(1);
281
+ process.exitCode = 1;
282
+ return;
281
283
  }
282
284
  // 2. Read manifest from installed package
283
285
  // Resolve the package name from the spec (could be a tarball path or name@version)
@@ -316,13 +318,18 @@ export async function marketSearchCommand(query, options = {}) {
316
318
  logger.newline();
317
319
  }
318
320
  try {
319
- const results = await searchPackages({ query, limit, registryUrl: registry });
321
+ let results = await searchPackages({ query, limit, registryUrl: registry });
322
+ // Client-side filtering: npm search may return broad results, narrow to query match
323
+ if (query) {
324
+ const q = query.toLowerCase();
325
+ results = results.filter((pkg) => pkg.name.toLowerCase().includes(q) || (pkg.description && pkg.description.toLowerCase().includes(q)));
326
+ }
320
327
  if (json) {
321
328
  console.log(JSON.stringify(results, null, 2));
322
329
  return;
323
330
  }
324
331
  if (results.length === 0) {
325
- logger.info('No packages found');
332
+ logger.info(query ? `No packages matching "${query}"` : 'No packages found');
326
333
  return;
327
334
  }
328
335
  for (const pkg of results) {
@@ -342,7 +349,8 @@ export async function marketSearchCommand(query, options = {}) {
342
349
  else {
343
350
  logger.error(`Search failed: ${getErrorMessage(err)}`);
344
351
  }
345
- process.exit(1);
352
+ process.exitCode = 1;
353
+ return;
346
354
  }
347
355
  }
348
356
  /**
@@ -25,6 +25,7 @@ export async function migrateCommand(globPattern, options = {}) {
25
25
  if (registeredMigrations.length > 0) {
26
26
  logger.info(`Registered edge-case migrations: ${registeredMigrations.map((m) => m.name).join(', ')}`);
27
27
  }
28
+ const t = logger.timer();
28
29
  let migratedCount = 0;
29
30
  let skippedCount = 0;
30
31
  let errorCount = 0;
@@ -80,10 +81,10 @@ export async function migrateCommand(globPattern, options = {}) {
80
81
  // Summary
81
82
  logger.newline();
82
83
  if (dryRun) {
83
- logger.info(`Dry run complete: ${migratedCount} file(s) would be updated, ${skippedCount} already current, ${errorCount} error(s)`);
84
+ logger.info(`Dry run: ${migratedCount} file(s) would be updated, ${skippedCount} already current, ${errorCount} error(s) in ${t.elapsed()}`);
84
85
  }
85
86
  else {
86
- logger.info(`Migration complete: ${migratedCount} file(s) updated, ${skippedCount} already current, ${errorCount} error(s)`);
87
+ logger.info(`${migratedCount} file(s) migrated, ${skippedCount} already current, ${errorCount} error(s) in ${t.elapsed()}`);
87
88
  }
88
89
  }
89
90
  //# sourceMappingURL=migrate.js.map
@@ -40,7 +40,10 @@ export async function openapiCommand(dir, options) {
40
40
  if (endpoints.length === 0) {
41
41
  throw new Error(`No workflows found in ${workflowDir}`);
42
42
  }
43
- logger.info(`Found ${endpoints.length} workflow(s)`);
43
+ // Only print info when writing to file (not stdout) to avoid contaminating JSON/YAML output
44
+ if (options.output) {
45
+ logger.info(`Found ${endpoints.length} workflow(s)`);
46
+ }
44
47
  // Generate options
45
48
  const generatorOptions = {
46
49
  title: options.title || 'Flow Weaver API',
@@ -13,6 +13,14 @@ import { logger } from '../utils/logger.js';
13
13
  import { getFriendlyError } from '../../friendly-errors.js';
14
14
  import { getErrorMessage } from '../../utils/error-utils.js';
15
15
  import { parseWorkflow } from '../../api/index.js';
16
+ /** Show path relative to cwd for cleaner output */
17
+ function displayPath(filePath) {
18
+ const rel = path.relative(process.cwd(), filePath);
19
+ if (rel && !rel.startsWith('..') && rel.length < filePath.length) {
20
+ return rel;
21
+ }
22
+ return filePath;
23
+ }
16
24
  /**
17
25
  * Execute a workflow file and output the result.
18
26
  *
@@ -38,10 +46,24 @@ import { parseWorkflow } from '../../api/index.js';
38
46
  * ```
39
47
  */
40
48
  export async function runCommand(input, options) {
49
+ // Wrap entire body in JSON-aware error handler when --json is set (0b fix)
50
+ if (options.json) {
51
+ try {
52
+ await runCommandInner(input, options);
53
+ }
54
+ catch (e) {
55
+ console.log(JSON.stringify({ success: false, error: getErrorMessage(e) }));
56
+ process.exitCode = 1;
57
+ }
58
+ return;
59
+ }
60
+ await runCommandInner(input, options);
61
+ }
62
+ async function runCommandInner(input, options) {
41
63
  const filePath = path.resolve(input);
42
64
  // Validate file exists
43
65
  if (!fs.existsSync(filePath)) {
44
- throw new Error(`File not found: ${filePath}`);
66
+ throw new Error(`File not found: ${displayPath(filePath)}`);
45
67
  }
46
68
  // Parse params from --params or --params-file
47
69
  let params = {};
@@ -119,7 +141,7 @@ export async function runCommand(input, options) {
119
141
  ? options.resume
120
142
  : findLatestCheckpoint(filePath, options.workflow);
121
143
  if (!checkpointPath) {
122
- throw new Error(`No checkpoint file found for ${filePath}. ` +
144
+ throw new Error(`No checkpoint file found for ${displayPath(filePath)}. ` +
123
145
  'Checkpoints are created when running with --checkpoint.');
124
146
  }
125
147
  const { data, stale, rerunNodes, skipNodes } = loadCheckpoint(checkpointPath, filePath);
@@ -147,9 +169,9 @@ export async function runCommand(input, options) {
147
169
  }
148
170
  }
149
171
  // Determine trace inclusion:
150
- // - If --production is set, no trace (unless --trace explicitly set)
151
- // - If --trace is set, include trace
152
- // - Default: include trace in dev mode
172
+ // Include trace data if --trace or --stream is explicitly set.
173
+ // Also include when not in production mode (needed for debug/checkpoint).
174
+ // Display of trace results is gated separately on options.trace/options.stream.
153
175
  const includeTrace = options.stream || options.trace || !options.production;
154
176
  if (!options.json && mocks) {
155
177
  logger.info('Running with mock data');
@@ -317,17 +339,20 @@ export async function runCommand(input, options) {
317
339
  logger.newline();
318
340
  logger.section('Result');
319
341
  logger.log(JSON.stringify(result.result, null, 2));
320
- if (result.trace && result.trace.length > 0) {
342
+ // Show trace summary only when --trace is explicitly set (not on --stream, which already printed live)
343
+ if (options.trace && !options.stream && result.trace && result.trace.length > 0) {
321
344
  logger.newline();
322
345
  logger.section('Trace');
323
346
  logger.log(`${result.trace.length} events captured`);
324
- // Show first few trace events as summary
325
- const preview = result.trace.slice(0, 5);
347
+ // Show first few trace events as summary (skip events without nodeId)
348
+ const meaningful = result.trace.filter((e) => e.data?.nodeId || e.data?.id);
349
+ const preview = meaningful.slice(0, 5);
326
350
  for (const event of preview) {
327
- logger.log(` [${event.type}] ${event.data?.nodeId || ''}`);
351
+ const nodeId = (event.data?.nodeId || event.data?.id || '');
352
+ logger.log(` [${event.type}] ${nodeId}`);
328
353
  }
329
- if (result.trace.length > 5) {
330
- logger.log(` ... and ${result.trace.length - 5} more events`);
354
+ if (meaningful.length > 5) {
355
+ logger.log(` ... and ${meaningful.length - 5} more events`);
331
356
  }
332
357
  }
333
358
  }
@@ -344,7 +369,7 @@ export async function runCommand(input, options) {
344
369
  const friendly = getFriendlyError(err);
345
370
  if (friendly) {
346
371
  logger.error(` ${friendly.title}: ${friendly.explanation}`);
347
- logger.info(` How to fix: ${friendly.fix}`);
372
+ logger.warn(` How to fix: ${friendly.fix}`);
348
373
  }
349
374
  else {
350
375
  logger.error(` - ${err.message}`);
@@ -355,7 +380,7 @@ export async function runCommand(input, options) {
355
380
  const friendly = getFriendlyError({ code: errorObj.code, message: errorMsg });
356
381
  if (friendly) {
357
382
  logger.error(`${friendly.title}: ${friendly.explanation}`);
358
- logger.info(` How to fix: ${friendly.fix}`);
383
+ logger.warn(` How to fix: ${friendly.fix}`);
359
384
  }
360
385
  else {
361
386
  logger.error(`Workflow execution failed: ${errorMsg}`);
@@ -364,11 +389,14 @@ export async function runCommand(input, options) {
364
389
  else {
365
390
  logger.error(`Workflow execution failed: ${errorMsg}`);
366
391
  }
367
- if (!options.json) {
368
- throw error;
392
+ if (options.json) {
393
+ // JSON mode: output structured error (0a fix: set exit code)
394
+ process.stdout.write(JSON.stringify({ success: false, error: errorMsg }, null, 2) + '\n');
395
+ process.exitCode = 1;
369
396
  }
370
397
  else {
371
- process.stdout.write(JSON.stringify({ success: false, error: errorMsg }, null, 2) + '\n');
398
+ // Non-json mode: don't re-throw to avoid duplicate error from wrapAction (0c fix)
399
+ process.exitCode = 1;
372
400
  }
373
401
  }
374
402
  finally {
@@ -23,7 +23,8 @@ export async function statusCommand(input, options = {}) {
23
23
  else {
24
24
  logger.error(`File not found: ${input}`);
25
25
  }
26
- process.exit(1);
26
+ process.exitCode = 1;
27
+ return;
27
28
  }
28
29
  const parseResult = await parseWorkflow(filePath, { workflowName });
29
30
  if (parseResult.errors.length > 0) {
@@ -34,7 +35,8 @@ export async function statusCommand(input, options = {}) {
34
35
  logger.error('Parse errors:');
35
36
  parseResult.errors.forEach((e) => logger.error(` ${e}`));
36
37
  }
37
- process.exit(1);
38
+ process.exitCode = 1;
39
+ return;
38
40
  }
39
41
  const ast = parseResult.ast;
40
42
  // Collect node statuses
@@ -115,7 +117,7 @@ export async function statusCommand(input, options = {}) {
115
117
  else {
116
118
  logger.error(`Status failed: ${getErrorMessage(error)}`);
117
119
  }
118
- process.exit(1);
120
+ process.exitCode = 1;
119
121
  }
120
122
  }
121
123
  //# sourceMappingURL=status.js.map
@@ -28,6 +28,7 @@ export async function stripCommand(input, options = {}) {
28
28
  logger.error(`No files found matching pattern: ${input}`);
29
29
  process.exit(1);
30
30
  }
31
+ const t = logger.timer();
31
32
  let stripped = 0;
32
33
  let skipped = 0;
33
34
  for (const filePath of files) {
@@ -57,6 +58,7 @@ export async function stripCommand(input, options = {}) {
57
58
  logger.success(`Stripped: ${path.relative(process.cwd(), outPath)}`);
58
59
  }
59
60
  }
60
- logger.success(`${stripped} file${stripped !== 1 ? 's' : ''} stripped, ${skipped} skipped`);
61
+ const verb = dryRun ? 'would be stripped' : 'stripped';
62
+ logger.success(`${stripped} file${stripped !== 1 ? 's' : ''} ${verb}, ${skipped} skipped in ${t.elapsed()}`);
61
63
  }
62
64
  //# sourceMappingURL=strip.js.map
@@ -48,7 +48,7 @@ export async function templatesCommand(options = {}) {
48
48
  logger.newline();
49
49
  logger.section("Usage");
50
50
  logger.log(" $ flow-weaver create workflow <template> <file> [--async] [--line N]");
51
- logger.log(" $ flow-weaver create node <template> <name> <file> [--line N]");
51
+ logger.log(" $ flow-weaver create node <name> <file> [-t <template>] [--line N]");
52
52
  logger.newline();
53
53
  }
54
54
  //# sourceMappingURL=templates.js.map
@@ -37,16 +37,18 @@ export async function validateCommand(input, options = {}) {
37
37
  if (files.length === 0) {
38
38
  if (json) {
39
39
  console.log(JSON.stringify({ error: `No files found matching pattern: ${input}` }));
40
+ process.exitCode = 1;
41
+ return;
40
42
  }
41
- else {
42
- logger.error(`No files found matching pattern: ${input}`);
43
- }
44
- process.exit(1);
43
+ throw new Error(`No files found matching pattern: ${input}`);
45
44
  }
45
+ const totalTimer = logger.timer();
46
46
  if (!json) {
47
47
  logger.section('Validating Workflows');
48
- logger.info(`Found ${files.length} file(s)`);
49
- logger.newline();
48
+ if (verbose) {
49
+ logger.info(`Found ${files.length} file(s)`);
50
+ logger.newline();
51
+ }
50
52
  }
51
53
  let totalErrors = 0;
52
54
  let totalWarnings = 0;
@@ -55,7 +57,7 @@ export async function validateCommand(input, options = {}) {
55
57
  for (let i = 0; i < files.length; i++) {
56
58
  const file = files[i];
57
59
  const fileName = path.basename(file);
58
- if (!json) {
60
+ if (!json && verbose) {
59
61
  logger.progress(i + 1, files.length, fileName);
60
62
  }
61
63
  try {
@@ -139,9 +141,9 @@ export async function validateCommand(input, options = {}) {
139
141
  if (friendly) {
140
142
  const loc = err.location ? `[line ${err.location.line}] ` : '';
141
143
  logger.error(` ${loc}${friendly.title}: ${friendly.explanation}`);
142
- logger.info(` How to fix: ${friendly.fix}`);
144
+ logger.warn(` How to fix: ${friendly.fix}`);
143
145
  if (err.docUrl) {
144
- logger.info(` See: ${err.docUrl}`);
146
+ logger.warn(` See: ${err.docUrl}`);
145
147
  }
146
148
  }
147
149
  else {
@@ -157,7 +159,7 @@ export async function validateCommand(input, options = {}) {
157
159
  }
158
160
  logger.error(msg);
159
161
  if (err.docUrl) {
160
- logger.info(` See: ${err.docUrl}`);
162
+ logger.warn(` See: ${err.docUrl}`);
161
163
  }
162
164
  }
163
165
  });
@@ -172,9 +174,9 @@ export async function validateCommand(input, options = {}) {
172
174
  if (friendly) {
173
175
  const loc = warn.location ? `[line ${warn.location.line}] ` : '';
174
176
  logger.warn(` ${loc}${friendly.title}: ${friendly.explanation}`);
175
- logger.info(` How to fix: ${friendly.fix}`);
177
+ logger.warn(` How to fix: ${friendly.fix}`);
176
178
  if (warn.docUrl) {
177
- logger.info(` See: ${warn.docUrl}`);
179
+ logger.warn(` See: ${warn.docUrl}`);
178
180
  }
179
181
  }
180
182
  else {
@@ -187,7 +189,7 @@ export async function validateCommand(input, options = {}) {
187
189
  }
188
190
  logger.warn(msg);
189
191
  if (warn.docUrl) {
190
- logger.info(` See: ${warn.docUrl}`);
192
+ logger.warn(` See: ${warn.docUrl}`);
191
193
  }
192
194
  }
193
195
  });
@@ -229,17 +231,19 @@ export async function validateCommand(input, options = {}) {
229
231
  }
230
232
  else {
231
233
  // Summary
234
+ const elapsed = totalTimer.elapsed();
232
235
  logger.newline();
233
- logger.section('Validation Summary');
234
- logger.success(`${successCount} file(s) valid`);
235
- if (totalWarnings > 0) {
236
- logger.warn(`${totalWarnings} warning(s) found`);
237
- }
238
236
  if (totalErrors > 0) {
239
- logger.error(`${totalErrors} error(s) found`);
237
+ const parts = [`${successCount} valid`, `${totalErrors} error${totalErrors !== 1 ? 's' : ''}`];
238
+ if (totalWarnings > 0)
239
+ parts.push(`${totalWarnings} warning${totalWarnings !== 1 ? 's' : ''}`);
240
+ logger.log(` ${parts.join(', ')} in ${elapsed}`);
241
+ }
242
+ else if (totalWarnings > 0) {
243
+ logger.log(` ${successCount} valid, ${totalWarnings} warning${totalWarnings !== 1 ? 's' : ''} in ${elapsed}`);
240
244
  }
241
245
  else {
242
- logger.success('All workflows are valid!');
246
+ logger.success(`${successCount} file${successCount !== 1 ? 's' : ''} valid in ${elapsed}`);
243
247
  }
244
248
  }
245
249
  if (totalErrors > 0) {
@@ -249,11 +253,10 @@ export async function validateCommand(input, options = {}) {
249
253
  catch (error) {
250
254
  if (json) {
251
255
  console.log(JSON.stringify({ error: getErrorMessage(error) }));
256
+ process.exitCode = 1;
257
+ return;
252
258
  }
253
- else {
254
- logger.error(`Validation failed: ${getErrorMessage(error)}`);
255
- }
256
- process.exit(1);
259
+ throw error;
257
260
  }
258
261
  }
259
262
  //# sourceMappingURL=validate.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Environment setup that must run before any color library imports.
3
+ * This module is imported first in index.ts to ensure picocolors reads the correct env vars.
4
+ */
5
+ //# sourceMappingURL=env-setup.d.ts.map
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ /**
3
+ * Environment setup that must run before any color library imports.
4
+ * This module is imported first in index.ts to ensure picocolors reads the correct env vars.
5
+ */
6
+ // FORCE_COLOR=0 should disable colors, but picocolors treats any non-empty
7
+ // FORCE_COLOR as "enable colors". Translate to NO_COLOR which picocolors checks first.
8
+ if (process.env.FORCE_COLOR === '0') {
9
+ process.env.NO_COLOR = '1';
10
+ delete process.env.FORCE_COLOR;
11
+ }
12
+ //# sourceMappingURL=env-setup.js.map