@output.ai/cli 0.3.1-dev.pr156.0 → 0.4.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 (50) hide show
  1. package/README.md +36 -197
  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/.gitignore.template +0 -3
  32. package/dist/templates/project/README.md.template +2 -2
  33. package/dist/templates/project/package.json.template +2 -2
  34. package/dist/templates/project/src/simple/scenarios/question_ada_lovelace.json.template +3 -0
  35. package/dist/templates/workflow/scenarios/test_input.json.template +7 -0
  36. package/dist/types/trace.d.ts +161 -0
  37. package/dist/types/trace.js +18 -0
  38. package/dist/utils/date_formatter.d.ts +8 -0
  39. package/dist/utils/date_formatter.js +19 -0
  40. package/dist/utils/template.spec.js +6 -0
  41. package/dist/utils/trace_formatter.d.ts +11 -61
  42. package/dist/utils/trace_formatter.js +384 -239
  43. package/package.json +6 -3
  44. package/dist/commands/workflow/debug.test.js +0 -107
  45. package/dist/commands/workflow/output.test.js +0 -23
  46. package/dist/utils/s3_downloader.d.ts +0 -49
  47. package/dist/utils/s3_downloader.js +0 -154
  48. /package/dist/commands/workflow/{debug.test.d.ts → debug.spec.d.ts} +0 -0
  49. /package/dist/commands/workflow/{output.test.d.ts → result.test.d.ts} +0 -0
  50. /package/dist/templates/workflow/{prompt@v1.prompt.template → prompts/prompt@v1.prompt.template} +0 -0
@@ -0,0 +1,36 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ // Mock the TraceReader service
3
+ vi.mock('../../services/trace_reader.js', () => ({
4
+ findTraceFile: vi.fn(),
5
+ readTraceFile: vi.fn(),
6
+ getTrace: vi.fn()
7
+ }));
8
+ // Mock the utilities
9
+ vi.mock('../../utils/trace_formatter.js', () => ({
10
+ displayDebugTree: vi.fn()
11
+ }));
12
+ describe('workflow debug command', () => {
13
+ beforeEach(() => {
14
+ vi.clearAllMocks();
15
+ });
16
+ describe('command definition', () => {
17
+ it('should export a valid OCLIF command', async () => {
18
+ const WorkflowDebug = (await import('./debug.js')).default;
19
+ expect(WorkflowDebug).toBeDefined();
20
+ expect(WorkflowDebug.description).toContain('Get and display workflow execution trace for debugging');
21
+ expect(WorkflowDebug.args).toHaveProperty('workflowId');
22
+ expect(WorkflowDebug.flags).toHaveProperty('format');
23
+ });
24
+ it('should have correct flag configuration', async () => {
25
+ const WorkflowDebug = (await import('./debug.js')).default;
26
+ // Format flag
27
+ expect(WorkflowDebug.flags.format.options).toEqual(['json', 'text']);
28
+ expect(WorkflowDebug.flags.format.default).toBe('text');
29
+ });
30
+ it('should have correct examples', async () => {
31
+ const WorkflowDebug = (await import('./debug.js')).default;
32
+ expect(WorkflowDebug.examples).toBeDefined();
33
+ expect(WorkflowDebug.examples.length).toBeGreaterThan(0);
34
+ });
35
+ });
36
+ });
@@ -2,6 +2,7 @@ import { Args, Command, Flags, ux } from '@oclif/core';
2
2
  import { generateWorkflow } from '#services/workflow_generator.js';
3
3
  import { buildWorkflow, buildWorkflowInteractiveLoop } from '#services/workflow_builder.js';
4
4
  import { ensureOutputAIStructure } from '#services/coding_agents.js';
5
+ import { getWorkflowGenerateSuccessMessage } from '#services/messages.js';
5
6
  import { DEFAULT_OUTPUT_DIRS } from '#utils/paths.js';
6
7
  import path from 'node:path';
7
8
  export default class Generate extends Command {
@@ -78,15 +79,7 @@ export default class Generate extends Command {
78
79
  }
79
80
  }
80
81
  displaySuccess(result) {
81
- this.log(`\n[SUCCESS] Workflow "${result.workflowName}" created successfully!`);
82
- this.log(`\nLocation: ${result.targetDir}`);
83
- this.log('\n-- Next steps --');
84
- this.log(` 1. cd ${result.targetDir}`);
85
- this.log(' 2. Edit the workflow files to customize your implementation');
86
- this.log(' 3. If using @output.ai/llm, configure your .env file with LLM provider credentials:');
87
- this.log(' - ANTHROPIC_API_KEY for Claude');
88
- this.log(' - OPENAI_API_KEY for OpenAI');
89
- this.log(' 4. Build and run with the worker');
90
- this.log('\nCheck the README.md for workflow-specific documentation.');
82
+ const message = getWorkflowGenerateSuccessMessage(result.workflowName, result.targetDir, result.filesCreated);
83
+ this.log(message);
91
84
  }
92
85
  }
@@ -46,7 +46,8 @@ describe('Generate Command', () => {
46
46
  skeleton: true,
47
47
  force: false
48
48
  });
49
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('[SUCCESS] Workflow "test-workflow" created successfully!'));
49
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('SUCCESS!'));
50
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('test-workflow'));
50
51
  });
51
52
  it('should require skeleton flag and reject without it', async () => {
52
53
  const cmd = createCommand();
@@ -105,9 +106,10 @@ describe('Generate Command', () => {
105
106
  filesCreated: ['index.ts', 'steps.ts', 'types.ts']
106
107
  });
107
108
  await cmd.run();
108
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('[SUCCESS] Workflow "my-workflow" created successfully!'));
109
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Location: /custom/path/my-workflow'));
110
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('-- Next steps --'));
109
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('SUCCESS!'));
110
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('my-workflow'));
111
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('/custom/path/my-workflow'));
112
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('NEXT STEPS'));
111
113
  });
112
114
  });
113
115
  });
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class WorkflowOutput extends Command {
2
+ export default class WorkflowResult extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
@@ -1,17 +1,17 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import { getWorkflowIdOutput } from '#api/generated/api.js';
2
+ import { getWorkflowIdResult } from '#api/generated/api.js';
3
3
  import { OUTPUT_FORMAT } from '#utils/constants.js';
4
4
  import { formatOutput } from '#utils/output_formatter.js';
5
5
  import { handleApiError } from '#utils/error_handler.js';
6
- export default class WorkflowOutput extends Command {
7
- static description = 'Get workflow execution output';
6
+ export default class WorkflowResult extends Command {
7
+ static description = 'Get workflow execution result';
8
8
  static examples = [
9
9
  '<%= config.bin %> <%= command.id %> wf-12345',
10
10
  '<%= config.bin %> <%= command.id %> wf-12345 --format json'
11
11
  ];
12
12
  static args = {
13
13
  workflowId: Args.string({
14
- description: 'The workflow ID to get output for',
14
+ description: 'The workflow ID to get result for',
15
15
  required: true
16
16
  })
17
17
  };
@@ -24,9 +24,9 @@ export default class WorkflowOutput extends Command {
24
24
  })
25
25
  };
26
26
  async run() {
27
- const { args, flags } = await this.parse(WorkflowOutput);
28
- this.log(`Fetching output for workflow: ${args.workflowId}...`);
29
- const response = await getWorkflowIdOutput(args.workflowId);
27
+ const { args, flags } = await this.parse(WorkflowResult);
28
+ this.log(`Fetching result for workflow: ${args.workflowId}...`);
29
+ const response = await getWorkflowIdResult(args.workflowId);
30
30
  if (!response || !response.data) {
31
31
  this.error('API returned invalid response', { exit: 1 });
32
32
  }
@@ -34,7 +34,7 @@ export default class WorkflowOutput extends Command {
34
34
  const lines = [
35
35
  `Workflow ID: ${result.workflowId || 'unknown'}`,
36
36
  '',
37
- 'Output:',
37
+ 'Result:',
38
38
  JSON.stringify(result.output, null, 2)
39
39
  ];
40
40
  return lines.join('\n');
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ vi.mock('../../api/generated/api.js', () => ({
3
+ getWorkflowIdResult: vi.fn()
4
+ }));
5
+ describe('workflow result command', () => {
6
+ beforeEach(() => {
7
+ vi.clearAllMocks();
8
+ });
9
+ describe('command definition', () => {
10
+ it('should export a valid OCLIF command', async () => {
11
+ const WorkflowResult = (await import('./result.js')).default;
12
+ expect(WorkflowResult).toBeDefined();
13
+ expect(WorkflowResult.description).toContain('Get workflow execution result');
14
+ expect(WorkflowResult.args).toHaveProperty('workflowId');
15
+ expect(WorkflowResult.flags).toHaveProperty('format');
16
+ });
17
+ it('should have correct flag configuration', async () => {
18
+ const WorkflowResult = (await import('./result.js')).default;
19
+ expect(WorkflowResult.flags.format.options).toEqual(['json', 'text']);
20
+ expect(WorkflowResult.flags.format.default).toBe('text');
21
+ });
22
+ });
23
+ });
@@ -45,7 +45,7 @@ export default class WorkflowStart extends Command {
45
45
  `Workflow ID: ${result.workflowId || 'unknown'}`,
46
46
  '',
47
47
  `Use "workflow status ${result.workflowId || '<workflow-id>'}" to check the workflow status`,
48
- `Use "workflow output ${result.workflowId || '<workflow-id>'}" to get the workflow output when complete`
48
+ `Use "workflow result ${result.workflowId || '<workflow-id>'}" to get the workflow result when complete`
49
49
  ].join('\n');
50
50
  this.log(`\n${output}`);
51
51
  }
@@ -29,6 +29,21 @@ export const AGENT_CONFIGS = {
29
29
  from: 'agents/workflow_planner.md.template',
30
30
  to: `${AGENT_CONFIG_DIR}/agents/workflow_planner.md`
31
31
  },
32
+ {
33
+ type: 'template',
34
+ from: 'agents/workflow_quality.md.template',
35
+ to: `${AGENT_CONFIG_DIR}/agents/workflow_quality.md`
36
+ },
37
+ {
38
+ type: 'template',
39
+ from: 'agents/context_fetcher.md.template',
40
+ to: `${AGENT_CONFIG_DIR}/agents/context_fetcher.md`
41
+ },
42
+ {
43
+ type: 'template',
44
+ from: 'agents/prompt_writer.md.template',
45
+ to: `${AGENT_CONFIG_DIR}/agents/prompt_writer.md`
46
+ },
32
47
  {
33
48
  type: 'template',
34
49
  from: 'commands/plan_workflow.md.template',
@@ -65,6 +80,21 @@ export const AGENT_CONFIGS = {
65
80
  from: `${AGENT_CONFIG_DIR}/agents/workflow_planner.md`,
66
81
  to: '.claude/agents/workflow_planner.md'
67
82
  },
83
+ {
84
+ type: 'symlink',
85
+ from: `${AGENT_CONFIG_DIR}/agents/workflow_quality.md`,
86
+ to: '.claude/agents/workflow_quality.md'
87
+ },
88
+ {
89
+ type: 'symlink',
90
+ from: `${AGENT_CONFIG_DIR}/agents/context_fetcher.md`,
91
+ to: '.claude/agents/context_fetcher.md'
92
+ },
93
+ {
94
+ type: 'symlink',
95
+ from: `${AGENT_CONFIG_DIR}/agents/prompt_writer.md`,
96
+ to: '.claude/agents/prompt_writer.md'
97
+ },
68
98
  {
69
99
  type: 'symlink',
70
100
  from: `${AGENT_CONFIG_DIR}/commands/plan_workflow.md`,
@@ -39,11 +39,11 @@ describe('coding_agents service', () => {
39
39
  const expectedCount = AGENT_CONFIGS.outputai.mappings.length +
40
40
  AGENT_CONFIGS['claude-code'].mappings.length;
41
41
  expect(files.length).toBe(expectedCount);
42
- expect(files.length).toBe(10);
42
+ expect(files.length).toBe(16);
43
43
  });
44
44
  it('should have outputai files with .outputai prefix', () => {
45
45
  const files = getRequiredFiles();
46
- const outputaiFiles = files.slice(0, 6);
46
+ const outputaiFiles = files.slice(0, 9);
47
47
  outputaiFiles.forEach(file => {
48
48
  expect(file).toMatch(/^\.outputai\//);
49
49
  });
@@ -77,12 +77,18 @@ describe('coding_agents service', () => {
77
77
  missingFiles: [
78
78
  '.outputai/AGENTS.md',
79
79
  '.outputai/agents/workflow_planner.md',
80
+ '.outputai/agents/workflow_quality.md',
81
+ '.outputai/agents/context_fetcher.md',
82
+ '.outputai/agents/prompt_writer.md',
80
83
  '.outputai/commands/plan_workflow.md',
81
84
  '.outputai/commands/build_workflow.md',
82
85
  '.outputai/meta/pre_flight.md',
83
86
  '.outputai/meta/post_flight.md',
84
87
  'CLAUDE.md',
85
88
  '.claude/agents/workflow_planner.md',
89
+ '.claude/agents/workflow_quality.md',
90
+ '.claude/agents/context_fetcher.md',
91
+ '.claude/agents/prompt_writer.md',
86
92
  '.claude/commands/plan_workflow.md',
87
93
  '.claude/commands/build_workflow.md'
88
94
  ],
@@ -98,54 +104,21 @@ describe('coding_agents service', () => {
98
104
  missingFiles: [],
99
105
  isComplete: true
100
106
  });
101
- expect(access).toHaveBeenCalledTimes(11); // dir + 6 outputai + 4 claude-code
107
+ expect(access).toHaveBeenCalledTimes(17); // dir + 9 outputai + 7 claude-code
102
108
  });
103
109
  it('should return missing files when some files do not exist', async () => {
104
- const calls = [];
105
- vi.mocked(access).mockImplementation(async () => {
106
- const callNum = calls.length + 1;
107
- calls.push(callNum);
108
- // Call 1: directory exists
109
- if (callNum === 1) {
110
- return undefined;
111
- }
112
- // Call 2: .outputai/AGENTS.md exists
113
- if (callNum === 2) {
114
- return undefined;
115
- }
116
- // Call 3: .outputai/agents/workflow_planner.md missing
117
- if (callNum === 3) {
118
- throw { code: 'ENOENT' };
119
- }
120
- // Call 4: .outputai/commands/plan_workflow.md exists
121
- if (callNum === 4) {
122
- return undefined;
123
- }
124
- // Call 5: .outputai/commands/build_workflow.md exists
125
- if (callNum === 5) {
126
- return undefined;
127
- }
128
- // Call 6: .outputai/meta/pre_flight.md exists
129
- if (callNum === 6) {
130
- return undefined;
131
- }
132
- // Call 7: .outputai/meta/post_flight.md exists
133
- if (callNum === 7) {
134
- return undefined;
135
- }
136
- // Call 8: CLAUDE.md exists
137
- if (callNum === 8) {
138
- return undefined;
139
- }
140
- // Call 9: .claude/agents/workflow_planner.md missing
141
- if (callNum === 9) {
142
- throw { code: 'ENOENT' };
143
- }
144
- // Call 10: .claude/commands/plan_workflow.md exists
145
- if (callNum === 10) {
146
- return undefined;
110
+ const missingFiles = new Set([
111
+ '.outputai/agents/workflow_planner.md',
112
+ '.claude/agents/workflow_planner.md'
113
+ ]);
114
+ vi.mocked(access).mockImplementation(async (path) => {
115
+ const pathStr = path.toString();
116
+ // Check if this path ends with any of the missing files
117
+ for (const missing of missingFiles) {
118
+ if (pathStr.endsWith(missing)) {
119
+ throw { code: 'ENOENT' };
120
+ }
147
121
  }
148
- // Call 11: .claude/commands/build_workflow.md exists
149
122
  return undefined;
150
123
  });
151
124
  const result = await checkAgentStructure('/test/project');
@@ -159,18 +132,20 @@ describe('coding_agents service', () => {
159
132
  });
160
133
  });
161
134
  it('should check all required files even when directory exists', async () => {
162
- vi.mocked(access)
163
- .mockResolvedValueOnce(undefined) // directory
164
- .mockResolvedValueOnce(undefined) // .outputai/AGENTS.md
165
- .mockRejectedValueOnce({ code: 'ENOENT' }) // .outputai/agents/workflow_planner.md
166
- .mockRejectedValueOnce({ code: 'ENOENT' }) // .outputai/commands/plan_workflow.md
167
- .mockResolvedValueOnce(undefined) // .outputai/commands/build_workflow.md
168
- .mockResolvedValueOnce(undefined) // .outputai/meta/pre_flight.md
169
- .mockResolvedValueOnce(undefined) // .outputai/meta/post_flight.md
170
- .mockResolvedValueOnce(undefined) // CLAUDE.md
171
- .mockResolvedValueOnce(undefined) // .claude/agents/workflow_planner.md
172
- .mockRejectedValueOnce({ code: 'ENOENT' }) // .claude/commands/plan_workflow.md
173
- .mockResolvedValueOnce(undefined); // .claude/commands/build_workflow.md
135
+ const missingFiles = new Set([
136
+ '.outputai/agents/workflow_planner.md',
137
+ '.outputai/commands/plan_workflow.md',
138
+ '.claude/commands/plan_workflow.md'
139
+ ]);
140
+ vi.mocked(access).mockImplementation(async (path) => {
141
+ const pathStr = path.toString();
142
+ for (const missing of missingFiles) {
143
+ if (pathStr.endsWith(missing)) {
144
+ throw { code: 'ENOENT' };
145
+ }
146
+ }
147
+ return undefined;
148
+ });
174
149
  const result = await checkAgentStructure('/test/project');
175
150
  expect(result.dirExists).toBe(true);
176
151
  expect(result.missingFiles).toEqual([
@@ -207,8 +182,8 @@ describe('coding_agents service', () => {
207
182
  });
208
183
  // Should create outputai files (6 templates)
209
184
  expect(fs.writeFile).toHaveBeenCalledWith(expect.stringContaining('AGENTS.md'), expect.any(String), 'utf-8');
210
- // Should create symlinks (4 symlinks for claude-code)
211
- expect(fs.symlink).toHaveBeenCalledTimes(4);
185
+ // Should create symlinks (7 symlinks for claude-code)
186
+ expect(fs.symlink).toHaveBeenCalledTimes(7);
212
187
  });
213
188
  it('should skip existing files when force is false', async () => {
214
189
  // Mock some files exist
@@ -3,3 +3,4 @@
3
3
  */
4
4
  export declare const getEjectSuccessMessage: (destPath: string, outputFile: string, binName: string) => string;
5
5
  export declare const getProjectSuccessMessage: (folderName: string, installSuccess: boolean, envConfigured?: boolean) => string;
6
+ export declare const getWorkflowGenerateSuccessMessage: (workflowName: string, targetDir: string, filesCreated: string[]) => string;
@@ -176,7 +176,7 @@ export const getProjectSuccessMessage = (folderName, installSuccess, envConfigur
176
176
  note: 'Launches Temporal, Redis, PostgreSQL, API, Worker, and UI'
177
177
  }, {
178
178
  step: 'Run example workflow',
179
- command: 'output workflow run simple --input \'{"question": "who really is ada lovelace?"}\'',
179
+ command: 'output workflow run simple --input src/simple/scenarios/question_ada_lovelace.json',
180
180
  note: 'Execute in a new terminal after services are running'
181
181
  }, {
182
182
  step: 'Monitor workflows',
@@ -231,3 +231,67 @@ ${ux.colorize('dim', ' with AI assistance.')}
231
231
  ${ux.colorize('green', ux.colorize('bold', 'Happy building with Output SDK! 🚀'))}
232
232
  `;
233
233
  };
234
+ export const getWorkflowGenerateSuccessMessage = (workflowName, targetDir, filesCreated) => {
235
+ const divider = ux.colorize('dim', '─'.repeat(80));
236
+ const bulletPoint = ux.colorize('green', '▸');
237
+ const formattedFiles = filesCreated.map(file => {
238
+ return ` ${bulletPoint} ${formatPath(file)}`;
239
+ }).join('\n');
240
+ const steps = [
241
+ {
242
+ step: 'Navigate to workflow directory',
243
+ command: `cd ${targetDir}`
244
+ },
245
+ {
246
+ step: 'Edit workflow files',
247
+ note: 'Customize workflow.ts, steps.ts, and prompts to match your requirements'
248
+ },
249
+ {
250
+ step: 'Configure environment',
251
+ command: 'Edit .env file',
252
+ note: 'Add your LLM provider credentials (ANTHROPIC_API_KEY or OPENAI_API_KEY)'
253
+ },
254
+ {
255
+ step: 'Test with example scenario',
256
+ command: `output workflow run ${workflowName} --input ${targetDir}/scenarios/test_input.json`,
257
+ note: 'Run after starting services with "output dev"'
258
+ }
259
+ ];
260
+ const formattedSteps = steps.map((item, index) => {
261
+ const stepNumber = ux.colorize('dim', `${index + 1}.`);
262
+ const stepText = ux.colorize('white', item.step);
263
+ const command = item.command ? `\n ${bulletPoint} ${formatCommand(item.command)}` : '';
264
+ const note = item.note ? `\n ${ux.colorize('dim', ` ${item.note}`)}` : '';
265
+ return ` ${stepNumber} ${stepText}${command}${note}`;
266
+ }).join('\n\n');
267
+ return `
268
+ ${divider}
269
+
270
+ ${ux.colorize('bold', ux.colorize('green', '✅ SUCCESS!'))} ${ux.colorize('bold', `Workflow "${workflowName}" created`)}
271
+
272
+ ${divider}
273
+
274
+ ${createSectionHeader('WORKFLOW DETAILS', '📁')}
275
+
276
+ ${bulletPoint} ${ux.colorize('white', 'Name:')} ${formatPath(workflowName)}
277
+ ${bulletPoint} ${ux.colorize('white', 'Location:')} ${formatPath(targetDir)}
278
+
279
+ ${divider}
280
+
281
+ ${createSectionHeader('FILES CREATED', '📄')}
282
+
283
+ ${formattedFiles}
284
+
285
+ ${divider}
286
+
287
+ ${createSectionHeader('NEXT STEPS', '🚀')}
288
+
289
+ ${formattedSteps}
290
+
291
+ ${divider}
292
+
293
+ ${ux.colorize('dim', '💡 Tip: Check the README.md in your workflow directory for detailed documentation.')}
294
+
295
+ ${ux.colorize('green', ux.colorize('bold', 'Happy building! 🛠️'))}
296
+ `;
297
+ };
@@ -0,0 +1,14 @@
1
+ import type { TraceData } from '#types/trace.js';
2
+ export type { TraceData };
3
+ /**
4
+ * Find trace file from workflow metadata
5
+ */
6
+ export declare function findTraceFile(workflowId: string): Promise<string>;
7
+ /**
8
+ * Read and parse trace file
9
+ */
10
+ export declare function readTraceFile(path: string): Promise<TraceData>;
11
+ /**
12
+ * Get trace data from workflow ID
13
+ */
14
+ export declare function getTrace(workflowId: string): Promise<TraceData>;
@@ -0,0 +1,67 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { getWorkflowIdResult } from '#api/generated/api.js';
3
+ import { getErrorCode } from '#utils/error_utils.js';
4
+ /**
5
+ * Check if a file exists with detailed error information
6
+ */
7
+ async function fileExists(path) {
8
+ try {
9
+ await stat(path);
10
+ return { exists: true };
11
+ }
12
+ catch (error) {
13
+ const code = getErrorCode(error);
14
+ if (code === 'ENOENT') {
15
+ return { exists: false };
16
+ }
17
+ if (code === 'EACCES') {
18
+ return { exists: false, error: `Permission denied: ${path}` };
19
+ }
20
+ return { exists: false, error: `Cannot access file: ${path}` };
21
+ }
22
+ }
23
+ /**
24
+ * Find trace file from workflow metadata
25
+ */
26
+ export async function findTraceFile(workflowId) {
27
+ const response = await getWorkflowIdResult(workflowId);
28
+ // Check if we got a successful response
29
+ if (response.status !== 200) {
30
+ throw new Error(`Failed to get workflow result for ${workflowId}`);
31
+ }
32
+ const tracePath = response.data.trace?.destinations?.local;
33
+ if (!tracePath) {
34
+ throw new Error(`No trace file path found for workflow ${workflowId}`);
35
+ }
36
+ const fileCheck = await fileExists(tracePath);
37
+ if (!fileCheck.exists) {
38
+ const errorDetail = fileCheck.error || `Trace file not found at path: ${tracePath}`;
39
+ throw new Error(errorDetail);
40
+ }
41
+ return tracePath;
42
+ }
43
+ /**
44
+ * Read and parse trace file
45
+ */
46
+ export async function readTraceFile(path) {
47
+ try {
48
+ const content = await readFile(path, 'utf-8');
49
+ return JSON.parse(content);
50
+ }
51
+ catch (error) {
52
+ if (getErrorCode(error) === 'ENOENT') {
53
+ throw new Error(`Trace file not found at path: ${path}`);
54
+ }
55
+ if (error instanceof SyntaxError) {
56
+ throw new Error(`Invalid JSON in trace file: ${path}`);
57
+ }
58
+ throw error;
59
+ }
60
+ }
61
+ /**
62
+ * Get trace data from workflow ID
63
+ */
64
+ export async function getTrace(workflowId) {
65
+ const tracePath = await findTraceFile(workflowId);
66
+ return readTraceFile(tracePath);
67
+ }
@@ -0,0 +1 @@
1
+ export {};