@output.ai/cli 0.3.1-dev.pr156.0 → 0.4.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.
Files changed (48) hide show
  1. package/README.md +2 -28
  2. package/dist/api/generated/api.d.ts +35 -59
  3. package/dist/api/generated/api.js +4 -13
  4. package/dist/assets/docker/docker-compose-dev.yml +2 -2
  5. package/dist/commands/workflow/debug.d.ts +2 -8
  6. package/dist/commands/workflow/debug.js +24 -164
  7. package/dist/commands/workflow/debug.spec.js +36 -0
  8. package/dist/commands/workflow/generate.js +3 -10
  9. package/dist/commands/workflow/generate.spec.js +6 -4
  10. package/dist/commands/workflow/{output.d.ts → result.d.ts} +1 -1
  11. package/dist/commands/workflow/{output.js → result.js} +8 -8
  12. package/dist/commands/workflow/result.test.js +23 -0
  13. package/dist/commands/workflow/start.js +1 -1
  14. package/dist/services/coding_agents.js +30 -0
  15. package/dist/services/coding_agents.spec.js +36 -61
  16. package/dist/services/messages.d.ts +1 -0
  17. package/dist/services/messages.js +65 -1
  18. package/dist/services/trace_reader.d.ts +14 -0
  19. package/dist/services/trace_reader.js +67 -0
  20. package/dist/services/trace_reader.spec.d.ts +1 -0
  21. package/dist/services/trace_reader.spec.js +164 -0
  22. package/dist/services/workflow_generator.spec.d.ts +1 -0
  23. package/dist/services/workflow_generator.spec.js +77 -0
  24. package/dist/templates/agent_instructions/AGENTS.md.template +209 -19
  25. package/dist/templates/agent_instructions/agents/context_fetcher.md.template +82 -0
  26. package/dist/templates/agent_instructions/agents/prompt_writer.md.template +595 -0
  27. package/dist/templates/agent_instructions/agents/workflow_planner.md.template +13 -4
  28. package/dist/templates/agent_instructions/agents/workflow_quality.md.template +244 -0
  29. package/dist/templates/agent_instructions/commands/build_workflow.md.template +52 -9
  30. package/dist/templates/agent_instructions/commands/plan_workflow.md.template +4 -4
  31. package/dist/templates/project/package.json.template +2 -2
  32. package/dist/templates/project/src/simple/scenarios/question_ada_lovelace.json.template +3 -0
  33. package/dist/templates/workflow/scenarios/test_input.json.template +7 -0
  34. package/dist/types/trace.d.ts +161 -0
  35. package/dist/types/trace.js +18 -0
  36. package/dist/utils/date_formatter.d.ts +8 -0
  37. package/dist/utils/date_formatter.js +19 -0
  38. package/dist/utils/template.spec.js +6 -0
  39. package/dist/utils/trace_formatter.d.ts +11 -61
  40. package/dist/utils/trace_formatter.js +384 -239
  41. package/package.json +2 -2
  42. package/dist/commands/workflow/debug.test.js +0 -107
  43. package/dist/commands/workflow/output.test.js +0 -23
  44. package/dist/utils/s3_downloader.d.ts +0 -49
  45. package/dist/utils/s3_downloader.js +0 -154
  46. /package/dist/commands/workflow/{debug.test.d.ts → debug.spec.d.ts} +0 -0
  47. /package/dist/commands/workflow/{output.test.d.ts → result.test.d.ts} +0 -0
  48. /package/dist/templates/workflow/{prompt@v1.prompt.template → prompts/prompt@v1.prompt.template} +0 -0
@@ -1,257 +1,402 @@
1
1
  import Table from 'cli-table3';
2
+ import { ux } from '@oclif/core';
2
3
  import { formatOutput } from '#utils/output_formatter.js';
3
- export class TraceFormatter {
4
- /**
5
- * Format trace data based on the requested format
6
- */
7
- format(traceData, format = 'text') {
8
- // Parse if string
9
- const trace = typeof traceData === 'string' ? JSON.parse(traceData) : traceData;
10
- if (format === 'json') {
11
- return formatOutput(trace, 'json');
4
+ import { formatDuration } from '#utils/date_formatter.js';
5
+ import { getErrorMessage } from '#utils/error_utils.js';
6
+ import { isTraceEvent, isValidTimestamp } from '#types/trace.js';
7
+ export { formatDuration };
8
+ const TRUNCATION = {
9
+ SHORT: 50,
10
+ STANDARD: 120,
11
+ SUFFIX: '...',
12
+ SUFFIX_VERBOSE: '... (truncated)'
13
+ };
14
+ const TREE_CHARS = {
15
+ BRANCH: '├─ ',
16
+ LAST: '└─ ',
17
+ VERTICAL: '│ ',
18
+ SPACE: ' ',
19
+ DETAIL_BRANCH: '│ ',
20
+ DETAIL_LAST: ' '
21
+ };
22
+ const HEADER_DIVIDER = '═'.repeat(60);
23
+ const colors = {
24
+ workflowHeader: (text) => ux.colorize('bold', ux.colorize('cyan', text)),
25
+ stepHeader: (text) => ux.colorize('yellow', text),
26
+ internalStep: (text) => ux.colorize('dim', text),
27
+ label: (text) => ux.colorize('blue', text),
28
+ error: (text) => ux.colorize('red', text),
29
+ success: (text) => ux.colorize('green', text)
30
+ };
31
+ const truncate = (value, maxLength = TRUNCATION.STANDARD, suffix = TRUNCATION.SUFFIX_VERBOSE, recursive = false) => {
32
+ if (value === null || value === undefined) {
33
+ return value;
34
+ }
35
+ if (typeof value === 'string') {
36
+ if (value.length <= maxLength) {
37
+ return value;
12
38
  }
13
- // Format as human-readable text
14
- return this.formatAsText(trace);
15
- }
16
- /**
17
- * Format trace as human-readable text with tree structure
18
- */
19
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
- formatAsText(trace) {
21
- const output = [];
22
- // Add header with workflow info
23
- if (trace.root) {
24
- output.push(this.formatHeader(trace.root));
25
- output.push('');
39
+ return `${value.substring(0, maxLength)}${suffix}`;
40
+ }
41
+ if (typeof value === 'number' || typeof value === 'boolean') {
42
+ if (recursive) {
43
+ return value;
26
44
  }
27
- // Create execution timeline table
28
- if (trace.events && trace.events.length > 0) {
29
- output.push('Execution Timeline:');
30
- output.push(this.formatEventsTable(trace.events));
31
- output.push('');
45
+ return String(value);
46
+ }
47
+ if (!recursive) {
48
+ const str = JSON.stringify(value);
49
+ if (str.length <= maxLength) {
50
+ return str;
32
51
  }
33
- // Show tree structure
34
- if (trace.root) {
35
- output.push('Execution Tree:');
36
- output.push(this.formatTree(trace.root, 0));
52
+ return `${str.substring(0, maxLength)}${suffix}`;
53
+ }
54
+ if (Array.isArray(value)) {
55
+ return value.map(item => truncate(item, maxLength, suffix, true));
56
+ }
57
+ if (typeof value === 'object') {
58
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, truncate(v, maxLength, suffix, true)]));
59
+ }
60
+ return value;
61
+ };
62
+ const truncateShort = (value) => {
63
+ return truncate(value, TRUNCATION.SHORT, TRUNCATION.SUFFIX, false);
64
+ };
65
+ const truncateRecursive = (value) => {
66
+ return truncate(value, TRUNCATION.STANDARD, TRUNCATION.SUFFIX_VERBOSE, true);
67
+ };
68
+ const formatPhase = (phase) => {
69
+ const phaseMap = {
70
+ start: '[START]',
71
+ end: '[END]',
72
+ error: '[ERROR]'
73
+ };
74
+ return phaseMap[phase] ?? phase;
75
+ };
76
+ const getNodeName = (node) => {
77
+ return node.name || node.workflowName || node.stepName || node.activityName || '';
78
+ };
79
+ const getNodeKind = (node) => {
80
+ return node.kind || node.type || '';
81
+ };
82
+ const getEventName = (event) => {
83
+ const { kind, workflowName, details } = event;
84
+ if (kind === 'workflow') {
85
+ return `Workflow: ${workflowName}`;
86
+ }
87
+ if (kind === 'activity') {
88
+ return `Activity: ${details?.activityName || 'unknown'}`;
89
+ }
90
+ if (kind === 'step') {
91
+ return `Step: ${details?.stepName || details?.name || 'unknown'}`;
92
+ }
93
+ return kind || 'Unknown Event';
94
+ };
95
+ const colorizeByKind = (kind, text) => {
96
+ if (kind === 'workflow') {
97
+ return colors.workflowHeader(text);
98
+ }
99
+ if (kind === 'internal_step') {
100
+ return colors.internalStep(text);
101
+ }
102
+ if (kind === 'step' || kind === 'activity') {
103
+ return colors.stepHeader(text);
104
+ }
105
+ return text;
106
+ };
107
+ const getStatusIndicator = (node) => {
108
+ if (node.phase === 'error' || node.status === 'failed') {
109
+ return colors.error('[FAILED]');
110
+ }
111
+ if (node.phase === 'end' || node.status === 'completed') {
112
+ return colors.success('[COMPLETED]');
113
+ }
114
+ if (node.status === 'running') {
115
+ return colors.label('[RUNNING]');
116
+ }
117
+ return '';
118
+ };
119
+ const getDebugNodeInfo = (node) => {
120
+ if (typeof node === 'string') {
121
+ return node;
122
+ }
123
+ if (typeof node !== 'object' || node === null) {
124
+ return String(node);
125
+ }
126
+ const debugNode = node;
127
+ const kind = getNodeKind(debugNode);
128
+ const name = getNodeName(debugNode);
129
+ const status = getStatusIndicator(debugNode);
130
+ const parts = [];
131
+ if (kind) {
132
+ parts.push(colorizeByKind(kind, `[${kind}]`));
133
+ }
134
+ if (name) {
135
+ parts.push(colorizeByKind(kind, name));
136
+ }
137
+ if (status) {
138
+ parts.push(status);
139
+ }
140
+ if (parts.length === 0) {
141
+ const keys = Object.keys(debugNode).filter(k => k !== 'children' && k !== 'parent');
142
+ if (keys.length > 0) {
143
+ return `Node {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? ', ...' : ''}}`;
37
144
  }
38
- return output.join('\n');
39
- }
40
- /**
41
- * Format the header with workflow information
42
- */
43
- formatHeader(root) {
44
- const lines = [];
45
- lines.push('═'.repeat(60));
46
- lines.push(`Workflow: ${root.workflowName}`);
47
- lines.push(`Workflow ID: ${root.workflowId}`);
48
- lines.push(`Start Time: ${new Date(root.timestamp).toISOString()}`);
49
- if (root.duration) {
50
- lines.push(`Duration: ${this.formatDuration(root.duration)}`);
145
+ return 'Node';
146
+ }
147
+ return parts.join(' ');
148
+ };
149
+ const extractNodeInfo = (node) => {
150
+ if (isTraceEvent(node)) {
151
+ return {
152
+ name: getEventName(node),
153
+ phase: formatPhase(node.phase),
154
+ duration: node.duration ? ` (${formatDuration(node.duration)})` : ''
155
+ };
156
+ }
157
+ return {
158
+ name: getDebugNodeInfo(node),
159
+ phase: node.phase ? formatPhase(node.phase) : '',
160
+ duration: node.duration ? ` (${formatDuration(node.duration)})` : ''
161
+ };
162
+ };
163
+ const formatDetails = (details) => {
164
+ if (!details) {
165
+ return '-';
166
+ }
167
+ if (typeof details === 'string') {
168
+ return details;
169
+ }
170
+ const info = [];
171
+ if (details.input) {
172
+ info.push(`Input: ${truncateShort(details.input)}`);
173
+ }
174
+ if (details.output) {
175
+ info.push(`Output: ${truncateShort(details.output)}`);
176
+ }
177
+ if (details.activityName) {
178
+ info.push(`Activity: ${details.activityName}`);
179
+ }
180
+ if (details.stepName || details.name) {
181
+ info.push(`Step: ${details.stepName || details.name}`);
182
+ }
183
+ if (info.length > 0) {
184
+ return info.join(', ');
185
+ }
186
+ return truncateShort(details);
187
+ };
188
+ const formatTreeDetails = (details, depth) => {
189
+ const indent = ' '.repeat(depth);
190
+ const lines = [];
191
+ if (details.input !== null && details.input !== undefined) {
192
+ lines.push(`${indent}Input: ${truncateShort(details.input)}`);
193
+ }
194
+ if (details.output !== null && details.output !== undefined) {
195
+ lines.push(`${indent}Output: ${truncateShort(details.output)}`);
196
+ }
197
+ return lines;
198
+ };
199
+ const formatValueWithIndent = (value, indentPrefix) => {
200
+ if (value === null || value === undefined) {
201
+ return String(value);
202
+ }
203
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
204
+ return truncate(value, TRUNCATION.STANDARD, TRUNCATION.SUFFIX_VERBOSE, false);
205
+ }
206
+ const truncated = truncateRecursive(value);
207
+ const jsonStr = JSON.stringify(truncated, null, 2);
208
+ const lines = jsonStr.split('\n');
209
+ if (lines.length <= 1) {
210
+ return jsonStr;
211
+ }
212
+ return lines.map((line, i) => {
213
+ if (i === 0) {
214
+ return line;
51
215
  }
52
- if (root.phase === 'error' && root.error) {
53
- lines.push('Status: ❌ Failed');
54
- lines.push(`Error: ${this.formatError(root.error)}`);
216
+ return indentPrefix + line;
217
+ }).join('\n');
218
+ };
219
+ const formatHeader = (root) => {
220
+ const isTrace = isTraceEvent(root);
221
+ const debugRoot = root;
222
+ const workflowName = isTrace ? root.workflowName : (getNodeName(debugRoot) || 'Unknown');
223
+ const workflowId = isTrace ? root.workflowId : 'N/A';
224
+ const timestamp = isTrace ? root.timestamp : debugRoot.startTime;
225
+ const phase = isTrace ? root.phase : debugRoot.phase;
226
+ const status = !isTrace ? debugRoot.status : undefined;
227
+ const lines = [
228
+ HEADER_DIVIDER,
229
+ `Workflow: ${workflowName}`,
230
+ `Workflow ID: ${workflowId}`
231
+ ];
232
+ if (isValidTimestamp(timestamp)) {
233
+ lines.push(`Start Time: ${new Date(timestamp).toISOString()}`);
234
+ }
235
+ if (root.duration) {
236
+ lines.push(`Duration: ${formatDuration(root.duration)}`);
237
+ }
238
+ if ((phase === 'error' || status === 'failed') && root.error) {
239
+ lines.push('Status: Failed', `Error: ${getErrorMessage(root.error)}`);
240
+ }
241
+ else if (phase === 'end' || status === 'completed') {
242
+ lines.push('Status: Completed');
243
+ }
244
+ else {
245
+ lines.push('Status: In Progress');
246
+ }
247
+ lines.push(HEADER_DIVIDER);
248
+ return lines.join('\n');
249
+ };
250
+ const formatEventsTable = (events) => {
251
+ const table = new Table({
252
+ head: ['Time', 'Event', 'Phase', 'Duration', 'Details'],
253
+ style: { head: ['cyan'] },
254
+ colWidths: [20, 25, 10, 12, null],
255
+ wordWrap: true
256
+ });
257
+ for (const event of events) {
258
+ table.push([
259
+ new Date(event.timestamp).toISOString().substring(11, 23),
260
+ getEventName(event),
261
+ formatPhase(event.phase),
262
+ event.duration ? formatDuration(event.duration) : '-',
263
+ formatDetails(event.details)
264
+ ]);
265
+ }
266
+ return table.toString();
267
+ };
268
+ const formatTree = (node, depth) => {
269
+ const indent = ' '.repeat(depth);
270
+ const marker = depth === 0 ? '' : TREE_CHARS.BRANCH;
271
+ const info = extractNodeInfo(node);
272
+ const lines = [`${indent}${marker} ${info.name} ${info.phase}${info.duration}`];
273
+ if (node.error) {
274
+ lines.push(`${indent} ${TREE_CHARS.LAST.trim()} ERROR: ${getErrorMessage(node.error)}`);
275
+ }
276
+ if (node.details && typeof node.details === 'object') {
277
+ lines.push(...formatTreeDetails(node.details, depth + 1));
278
+ }
279
+ if (node.children && node.children.length > 0) {
280
+ for (const child of node.children) {
281
+ lines.push(...formatTree(child, depth + 1));
55
282
  }
56
- else if (root.phase === 'end') {
57
- lines.push('Status: ✅ Completed');
283
+ }
284
+ return lines;
285
+ };
286
+ const getDebugNodeDetails = (node, prefix) => {
287
+ if (typeof node !== 'object' || node === null) {
288
+ return [];
289
+ }
290
+ const details = [];
291
+ const startedAt = node.startedAt || node.timestamp;
292
+ const inputIndentPrefix = prefix + ' '.repeat(7);
293
+ const outputIndentPrefix = prefix + ' '.repeat(8);
294
+ if (isValidTimestamp(startedAt)) {
295
+ const startDate = new Date(startedAt);
296
+ if (!isNaN(startDate.getTime())) {
297
+ details.push(`${prefix}${colors.label('Started:')} ${startDate.toISOString()}`);
58
298
  }
59
- else {
60
- lines.push('Status: 🔄 In Progress');
299
+ }
300
+ if (isValidTimestamp(node.endedAt)) {
301
+ const endDate = new Date(node.endedAt);
302
+ if (!isNaN(endDate.getTime())) {
303
+ details.push(`${prefix}${colors.label('Ended:')} ${endDate.toISOString()}`);
61
304
  }
62
- lines.push('═'.repeat(60));
63
- return lines.join('\n');
64
- }
65
- /**
66
- * Format events as a timeline table
67
- */
68
- formatEventsTable(events) {
69
- const table = new Table({
70
- head: ['Time', 'Event', 'Phase', 'Duration', 'Details'],
71
- style: {
72
- head: ['cyan']
73
- },
74
- colWidths: [20, 25, 10, 12, null],
75
- wordWrap: true
76
- });
77
- for (const event of events) {
78
- const time = new Date(event.timestamp).toISOString().substring(11, 23);
79
- const eventName = this.getEventName(event);
80
- const phase = this.formatPhase(event.phase);
81
- const duration = event.duration ? this.formatDuration(event.duration) : '-';
82
- const details = this.formatDetails(event.details);
83
- table.push([time, eventName, phase, duration, details]);
305
+ }
306
+ if (typeof node.startedAt === 'number' && typeof node.endedAt === 'number') {
307
+ details.push(`${prefix}${colors.label('Duration:')} ${formatDuration(node.endedAt - node.startedAt)}`);
308
+ }
309
+ else if (node.duration) {
310
+ details.push(`${prefix}${colors.label('Duration:')} ${formatDuration(node.duration)}`);
311
+ }
312
+ if (node.input !== null && node.input !== undefined) {
313
+ details.push(`${prefix}${colors.label('Input:')} ${formatValueWithIndent(node.input, inputIndentPrefix)}`);
314
+ }
315
+ if (node.output !== null && node.output !== undefined) {
316
+ details.push(`${prefix}${colors.label('Output:')} ${formatValueWithIndent(node.output, outputIndentPrefix)}`);
317
+ }
318
+ if (node.error) {
319
+ details.push(`${prefix}${colors.error('Error:')} ${colors.error(getErrorMessage(node.error))}`);
320
+ }
321
+ if (details.length > 0) {
322
+ details.push('');
323
+ }
324
+ return details;
325
+ };
326
+ const buildDebugTreeLines = (node, depth, isLast, prefix) => {
327
+ if (node === null || node === undefined) {
328
+ return [];
329
+ }
330
+ const isRoot = depth === 0;
331
+ const getConnector = () => {
332
+ if (isRoot) {
333
+ return '';
84
334
  }
85
- return table.toString();
86
- }
87
- /**
88
- * Format trace as a tree structure
89
- */
90
- formatTree(node, depth) {
91
- const lines = [];
92
- const indent = ' '.repeat(depth);
93
- const marker = depth === 0 ? '🌳' : '├─';
94
- // Format current node
95
- const nodeName = this.getEventName(node);
96
- const phase = this.formatPhase(node.phase);
97
- const duration = node.duration ? ` (${this.formatDuration(node.duration)})` : '';
98
- lines.push(`${indent}${marker} ${nodeName} ${phase}${duration}`);
99
- // Add error details if present
100
- if (node.error) {
101
- lines.push(`${indent} └─ ❌ ${this.formatError(node.error)}`);
335
+ return isLast ? TREE_CHARS.LAST : TREE_CHARS.BRANCH;
336
+ };
337
+ const connector = getConnector();
338
+ const indent = isRoot ? '' : prefix + connector;
339
+ const lines = [indent + getDebugNodeInfo(node)];
340
+ const detailPrefix = isRoot ? ' ' : prefix + (isLast ? TREE_CHARS.DETAIL_LAST : TREE_CHARS.DETAIL_BRANCH);
341
+ if (typeof node === 'object' && node !== null) {
342
+ lines.push(...getDebugNodeDetails(node, detailPrefix));
343
+ }
344
+ const childPrefix = isRoot ? '' : prefix + (isLast ? TREE_CHARS.SPACE : TREE_CHARS.VERTICAL);
345
+ if (typeof node === 'object' && node !== null) {
346
+ const debugNode = node;
347
+ if (Array.isArray(debugNode.children)) {
348
+ debugNode.children.forEach((child, i) => {
349
+ const isLastChild = i === debugNode.children.length - 1;
350
+ lines.push(...buildDebugTreeLines(child, depth + 1, isLastChild, childPrefix));
351
+ });
102
352
  }
103
- // Add important details
104
- if (node.details && typeof node.details === 'object') {
105
- const detailLines = this.formatTreeDetails(node.details, depth + 1);
106
- if (detailLines) {
107
- lines.push(detailLines);
353
+ }
354
+ return lines;
355
+ };
356
+ const formatAsText = (trace) => {
357
+ const output = [];
358
+ if (trace.root) {
359
+ output.push(formatHeader(trace.root), '');
360
+ }
361
+ if (trace.events && trace.events.length > 0) {
362
+ output.push('Execution Timeline:', formatEventsTable(trace.events), '');
363
+ }
364
+ if (trace.root) {
365
+ output.push('Execution Tree:', ...formatTree(trace.root, 0));
366
+ }
367
+ return output.join('\n');
368
+ };
369
+ export function format(traceData, outputFormat = 'text') {
370
+ const trace = typeof traceData === 'string' ? JSON.parse(traceData) : traceData;
371
+ if (outputFormat === 'json') {
372
+ return formatOutput(trace, 'json');
373
+ }
374
+ return formatAsText(trace);
375
+ }
376
+ export function getSummary(traceData) {
377
+ const trace = typeof traceData === 'string' ? JSON.parse(traceData) : traceData;
378
+ const stats = {
379
+ totalDuration: trace.root?.duration || 0,
380
+ totalEvents: trace.events?.length || 0,
381
+ totalSteps: 0,
382
+ totalActivities: 0,
383
+ hasErrors: false
384
+ };
385
+ if (trace.events) {
386
+ for (const event of trace.events) {
387
+ if (event.kind === 'step') {
388
+ stats.totalSteps++;
108
389
  }
109
- }
110
- // Process children
111
- if (node.children && node.children.length > 0) {
112
- for (const child of node.children) {
113
- lines.push(this.formatTree(child, depth + 1));
390
+ if (event.kind === 'activity') {
391
+ stats.totalActivities++;
114
392
  }
115
- }
116
- return lines.join('\n');
117
- }
118
- /**
119
- * Get a readable name for an event
120
- */
121
- getEventName(event) {
122
- if (event.kind === 'workflow') {
123
- return `Workflow: ${event.workflowName}`;
124
- }
125
- if (event.kind === 'activity') {
126
- return `Activity: ${event.details?.activityName || 'unknown'}`;
127
- }
128
- if (event.kind === 'step') {
129
- return `Step: ${event.details?.stepName || event.details?.name || 'unknown'}`;
130
- }
131
- return event.kind || 'Unknown Event';
132
- }
133
- /**
134
- * Format the phase with icons
135
- */
136
- formatPhase(phase) {
137
- switch (phase) {
138
- case 'start':
139
- return '▶️';
140
- case 'end':
141
- return '✅';
142
- case 'error':
143
- return '❌';
144
- default:
145
- return phase;
146
- }
147
- }
148
- /**
149
- * Format duration in human-readable format
150
- */
151
- formatDuration(ms) {
152
- if (ms < 1000) {
153
- return `${ms}ms`;
154
- }
155
- if (ms < 60000) {
156
- return `${(ms / 1000).toFixed(2)}s`;
157
- }
158
- const minutes = Math.floor(ms / 60000);
159
- const seconds = ((ms % 60000) / 1000).toFixed(0);
160
- return `${minutes}m ${seconds}s`;
161
- }
162
- /**
163
- * Format error for display
164
- */
165
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
- formatError(error) {
167
- if (typeof error === 'string') {
168
- return error;
169
- }
170
- if (error.message) {
171
- return error.message;
172
- }
173
- return JSON.stringify(error);
174
- }
175
- /**
176
- * Format details for table display
177
- */
178
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
- formatDetails(details) {
180
- if (!details) {
181
- return '-';
182
- }
183
- if (typeof details === 'string') {
184
- return details;
185
- }
186
- // Extract key information
187
- const info = [];
188
- if (details.input) {
189
- info.push(`Input: ${this.truncateValue(details.input)}`);
190
- }
191
- if (details.output) {
192
- info.push(`Output: ${this.truncateValue(details.output)}`);
193
- }
194
- if (details.activityName) {
195
- info.push(`Activity: ${details.activityName}`);
196
- }
197
- if (details.stepName || details.name) {
198
- info.push(`Step: ${details.stepName || details.name}`);
199
- }
200
- return info.length > 0 ? info.join(', ') : JSON.stringify(details).substring(0, 50) + '...';
201
- }
202
- /**
203
- * Format details for tree display
204
- */
205
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
206
- formatTreeDetails(details, depth) {
207
- const lines = [];
208
- const indent = ' '.repeat(depth);
209
- // Only show key details
210
- if (details.input && details.input !== null) {
211
- lines.push(`${indent}📥 Input: ${this.truncateValue(details.input)}`);
212
- }
213
- if (details.output && details.output !== null) {
214
- lines.push(`${indent}📤 Output: ${this.truncateValue(details.output)}`);
215
- }
216
- return lines.join('\n');
217
- }
218
- /**
219
- * Truncate long values for display
220
- */
221
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
222
- truncateValue(value, maxLength = 50) {
223
- const str = typeof value === 'string' ? value : JSON.stringify(value);
224
- if (str.length <= maxLength) {
225
- return str;
226
- }
227
- return str.substring(0, maxLength) + '...';
228
- }
229
- /**
230
- * Get summary statistics from trace
231
- */
232
- getSummary(traceData) {
233
- const trace = typeof traceData === 'string' ? JSON.parse(traceData) : traceData;
234
- const stats = {
235
- totalDuration: trace.root?.duration || 0,
236
- totalEvents: trace.events?.length || 0,
237
- totalSteps: 0,
238
- totalActivities: 0,
239
- hasErrors: false
240
- };
241
- if (trace.events) {
242
- for (const event of trace.events) {
243
- if (event.kind === 'step') {
244
- stats.totalSteps++;
245
- }
246
- if (event.kind === 'activity') {
247
- stats.totalActivities++;
248
- }
249
- if (event.phase === 'error') {
250
- stats.hasErrors = true;
251
- }
393
+ if (event.phase === 'error') {
394
+ stats.hasErrors = true;
252
395
  }
253
396
  }
254
- return stats;
255
397
  }
398
+ return stats;
399
+ }
400
+ export function displayDebugTree(node) {
401
+ return buildDebugTreeLines(node, 0, false, '').join('\n');
256
402
  }
257
- export const traceFormatter = new TraceFormatter();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/cli",
3
- "version": "0.3.1-dev.pr156.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI for Output.ai workflow generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,7 +22,6 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@anthropic-ai/claude-agent-sdk": "0.1.19",
25
- "@aws-sdk/client-s3": "^3.689.0",
26
25
  "@inquirer/prompts": "7.9.0",
27
26
  "@oclif/core": "4.5.6",
28
27
  "@oclif/plugin-help": "6.2.33",
@@ -31,6 +30,7 @@
31
30
  "change-case": "5.4.4",
32
31
  "cli-progress": "3.12.0",
33
32
  "cli-table3": "0.6.5",
33
+ "date-fns": "4.1.0",
34
34
  "dotenv": "16.4.7",
35
35
  "handlebars": "4.7.8",
36
36
  "json-schema-library": "10.3.0",