@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,107 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- // Mock the API
3
- vi.mock('../../api/generated/api.js', () => ({
4
- getWorkflowIdOutput: vi.fn()
5
- }));
6
- // Mock the utilities
7
- vi.mock('../../utils/s3_downloader.js', () => ({
8
- s3Downloader: {
9
- isAvailable: vi.fn(),
10
- download: vi.fn(),
11
- clearCache: vi.fn(),
12
- getCacheSize: vi.fn()
13
- },
14
- S3Downloader: vi.fn()
15
- }));
16
- vi.mock('../../utils/trace_formatter.js', () => ({
17
- traceFormatter: {
18
- format: vi.fn(),
19
- getSummary: vi.fn()
20
- },
21
- TraceFormatter: vi.fn()
22
- }));
23
- // Note: fs operations are mocked but not used in simplified tests
24
- // Real OCLIF testing would require complex framework initialization
25
- describe('workflow debug command', () => {
26
- beforeEach(() => {
27
- vi.clearAllMocks();
28
- });
29
- describe('command definition', () => {
30
- it('should export a valid OCLIF command', async () => {
31
- const WorkflowDebug = (await import('./debug.js')).default;
32
- expect(WorkflowDebug).toBeDefined();
33
- expect(WorkflowDebug.description).toContain('Get and display workflow execution trace for debugging');
34
- expect(WorkflowDebug.args).toHaveProperty('workflowId');
35
- expect(WorkflowDebug.flags).toHaveProperty('format');
36
- expect(WorkflowDebug.flags).toHaveProperty('remote');
37
- expect(WorkflowDebug.flags).toHaveProperty('download-dir');
38
- expect(WorkflowDebug.flags).toHaveProperty('open');
39
- expect(WorkflowDebug.flags).toHaveProperty('force-download');
40
- });
41
- it('should have correct flag configuration', async () => {
42
- const WorkflowDebug = (await import('./debug.js')).default;
43
- // Format flag
44
- expect(WorkflowDebug.flags.format.options).toEqual(['json', 'text']);
45
- expect(WorkflowDebug.flags.format.default).toBe('text');
46
- // Remote flag
47
- expect(WorkflowDebug.flags.remote.default).toBe(false);
48
- // Download dir flag
49
- expect(WorkflowDebug.flags['download-dir'].default).toContain('.output/traces');
50
- // Open flag
51
- expect(WorkflowDebug.flags.open.default).toBe(false);
52
- // Force download flag
53
- expect(WorkflowDebug.flags['force-download'].default).toBe(false);
54
- });
55
- it('should have correct examples', async () => {
56
- const WorkflowDebug = (await import('./debug.js')).default;
57
- expect(WorkflowDebug.examples).toBeDefined();
58
- expect(WorkflowDebug.examples.length).toBeGreaterThan(0);
59
- });
60
- });
61
- describe('run method', () => {
62
- // Variables will be initialized in beforeEach
63
- // eslint-disable-next-line init-declarations, no-restricted-syntax, @typescript-eslint/no-explicit-any
64
- let WorkflowDebug;
65
- // eslint-disable-next-line init-declarations, no-restricted-syntax, @typescript-eslint/no-explicit-any
66
- let getWorkflowIdOutput;
67
- beforeEach(async () => {
68
- const apiModule = await import('../../api/generated/api.js');
69
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
- getWorkflowIdOutput = apiModule.getWorkflowIdOutput;
71
- WorkflowDebug = (await import('./debug.js')).default;
72
- });
73
- it('should fetch and display local trace when available', async () => {
74
- // This test requires OCLIF framework initialization which is complex to mock
75
- // The functionality is tested through manual testing and the build process
76
- expect(true).toBe(true);
77
- });
78
- it('should download and display remote trace when local not available', async () => {
79
- // This test requires OCLIF framework initialization which is complex to mock
80
- // The functionality is tested through manual testing and the build process
81
- expect(true).toBe(true);
82
- });
83
- it('should prefer remote trace when --remote flag is set', async () => {
84
- // This test requires OCLIF framework initialization which is complex to mock
85
- // The functionality is tested through manual testing and the build process
86
- expect(true).toBe(true);
87
- });
88
- it('should handle workflow not found error', async () => {
89
- getWorkflowIdOutput.mockRejectedValue({ response: { status: 404 } });
90
- const cmd = new WorkflowDebug(['non-existent-workflow'], {});
91
- cmd.error = vi.fn();
92
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
- await cmd.catch({ response: { status: 404 } });
94
- expect(cmd.error).toHaveBeenCalledWith(expect.stringContaining('Workflow not found'), expect.any(Object));
95
- });
96
- it('should handle missing AWS credentials', async () => {
97
- // This test requires OCLIF framework initialization which is complex to mock
98
- // The functionality is tested through manual testing and the build process
99
- expect(true).toBe(true);
100
- });
101
- it('should save trace file when --open flag is set', async () => {
102
- // This test requires OCLIF framework initialization which is complex to mock
103
- // The functionality is tested through manual testing and the build process
104
- expect(true).toBe(true);
105
- });
106
- });
107
- });
@@ -1,23 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- vi.mock('../../api/generated/api.js', () => ({
3
- getWorkflowIdOutput: vi.fn()
4
- }));
5
- describe('workflow output command', () => {
6
- beforeEach(() => {
7
- vi.clearAllMocks();
8
- });
9
- describe('command definition', () => {
10
- it('should export a valid OCLIF command', async () => {
11
- const WorkflowOutput = (await import('./output.js')).default;
12
- expect(WorkflowOutput).toBeDefined();
13
- expect(WorkflowOutput.description).toContain('Get workflow execution output');
14
- expect(WorkflowOutput.args).toHaveProperty('workflowId');
15
- expect(WorkflowOutput.flags).toHaveProperty('format');
16
- });
17
- it('should have correct flag configuration', async () => {
18
- const WorkflowOutput = (await import('./output.js')).default;
19
- expect(WorkflowOutput.flags.format.options).toEqual(['json', 'text']);
20
- expect(WorkflowOutput.flags.format.default).toBe('text');
21
- });
22
- });
23
- });
@@ -1,49 +0,0 @@
1
- interface S3DownloadOptions {
2
- cacheDir?: string;
3
- forceDownload?: boolean;
4
- }
5
- export declare class S3Downloader {
6
- private s3Client;
7
- private cacheDir;
8
- constructor(options?: S3DownloadOptions);
9
- private initializeS3Client;
10
- /**
11
- * Parse S3 URL to extract bucket and key
12
- * Supports formats:
13
- * - https://bucket.s3.amazonaws.com/path/to/file
14
- * - https://bucket.s3.region.amazonaws.com/path/to/file
15
- * - s3://bucket/path/to/file
16
- */
17
- private parseS3Url;
18
- /**
19
- * Get cached file path for a given S3 URL
20
- */
21
- private getCachePath;
22
- /**
23
- * Download a file from S3
24
- */
25
- download(s3Url: string, options?: {
26
- forceDownload?: boolean;
27
- }): Promise<string>;
28
- /**
29
- * Check if S3 client is available (credentials configured)
30
- */
31
- isAvailable(): boolean;
32
- /**
33
- * Ensure cache directory exists
34
- */
35
- private ensureCacheDir;
36
- /**
37
- * Clear the cache directory
38
- */
39
- clearCache(): Promise<void>;
40
- /**
41
- * Get the size of the cache directory in bytes
42
- */
43
- getCacheSize(): Promise<number>;
44
- }
45
- /**
46
- * Create a singleton instance for convenience
47
- */
48
- export declare const s3Downloader: S3Downloader;
49
- export {};
@@ -1,154 +0,0 @@
1
- import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
2
- import * as fs from 'node:fs/promises';
3
- import * as path from 'node:path';
4
- import { existsSync } from 'node:fs';
5
- export class S3Downloader {
6
- s3Client = null;
7
- cacheDir;
8
- constructor(options = {}) {
9
- this.cacheDir = options.cacheDir || path.join(process.cwd(), '.output', 'traces');
10
- this.initializeS3Client();
11
- }
12
- initializeS3Client() {
13
- const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
14
- const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
15
- const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
16
- if (!accessKeyId || !secretAccessKey) {
17
- // S3 client not available without credentials
18
- this.s3Client = null;
19
- return;
20
- }
21
- this.s3Client = new S3Client({
22
- region,
23
- credentials: {
24
- accessKeyId,
25
- secretAccessKey
26
- }
27
- });
28
- }
29
- /**
30
- * Parse S3 URL to extract bucket and key
31
- * Supports formats:
32
- * - https://bucket.s3.amazonaws.com/path/to/file
33
- * - https://bucket.s3.region.amazonaws.com/path/to/file
34
- * - s3://bucket/path/to/file
35
- */
36
- parseS3Url(url) {
37
- // Handle s3:// protocol
38
- if (url.startsWith('s3://')) {
39
- const withoutProtocol = url.slice(5);
40
- const firstSlash = withoutProtocol.indexOf('/');
41
- if (firstSlash === -1) {
42
- throw new Error(`Invalid S3 URL format: ${url}`);
43
- }
44
- return {
45
- bucket: withoutProtocol.substring(0, firstSlash),
46
- key: withoutProtocol.substring(firstSlash + 1)
47
- };
48
- }
49
- // Handle https:// URLs
50
- if (url.startsWith('https://')) {
51
- const urlObj = new URL(url);
52
- const hostname = urlObj.hostname;
53
- // Extract bucket from hostname
54
- // Format: bucket.s3.amazonaws.com or bucket.s3.region.amazonaws.com
55
- const s3Match = hostname.match(/^([^.]+)\.s3(?:\.[^.]+)?\.amazonaws\.com$/);
56
- if (!s3Match) {
57
- throw new Error(`Invalid S3 URL format: ${url}`);
58
- }
59
- const bucket = s3Match[1];
60
- const key = urlObj.pathname.startsWith('/') ? urlObj.pathname.slice(1) : urlObj.pathname;
61
- return { bucket, key };
62
- }
63
- throw new Error(`Unsupported S3 URL format: ${url}`);
64
- }
65
- /**
66
- * Get cached file path for a given S3 URL
67
- */
68
- getCachePath(url) {
69
- const { key } = this.parseS3Url(url);
70
- // Use the last part of the key as filename to preserve the original name
71
- const filename = path.basename(key);
72
- return path.join(this.cacheDir, filename);
73
- }
74
- /**
75
- * Download a file from S3
76
- */
77
- async download(s3Url, options = {}) {
78
- if (!this.s3Client) {
79
- throw new Error('AWS credentials not configured. Please set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.');
80
- }
81
- // Check cache first
82
- const cachePath = this.getCachePath(s3Url);
83
- if (!options.forceDownload && existsSync(cachePath)) {
84
- const content = await fs.readFile(cachePath, 'utf-8');
85
- return content;
86
- }
87
- // Parse S3 URL
88
- const { bucket, key } = this.parseS3Url(s3Url);
89
- try {
90
- // Download from S3
91
- const command = new GetObjectCommand({
92
- Bucket: bucket,
93
- Key: key
94
- });
95
- const response = await this.s3Client.send(command);
96
- if (!response.Body) {
97
- throw new Error('Empty response from S3');
98
- }
99
- // Convert stream to string
100
- const bodyContents = await response.Body.transformToString();
101
- // Cache the file
102
- await this.ensureCacheDir();
103
- await fs.writeFile(cachePath, bodyContents, 'utf-8');
104
- return bodyContents;
105
- }
106
- catch (error) {
107
- if (error instanceof Error) {
108
- throw new Error(`Failed to download from S3: ${error.message}`);
109
- }
110
- throw error;
111
- }
112
- }
113
- /**
114
- * Check if S3 client is available (credentials configured)
115
- */
116
- isAvailable() {
117
- return this.s3Client !== null;
118
- }
119
- /**
120
- * Ensure cache directory exists
121
- */
122
- async ensureCacheDir() {
123
- await fs.mkdir(this.cacheDir, { recursive: true });
124
- }
125
- /**
126
- * Clear the cache directory
127
- */
128
- async clearCache() {
129
- if (existsSync(this.cacheDir)) {
130
- await fs.rm(this.cacheDir, { recursive: true, force: true });
131
- }
132
- }
133
- /**
134
- * Get the size of the cache directory in bytes
135
- */
136
- async getCacheSize() {
137
- if (!existsSync(this.cacheDir)) {
138
- return 0;
139
- }
140
- const files = await fs.readdir(this.cacheDir);
141
- // eslint-disable-next-line no-restricted-syntax
142
- let totalSize = 0;
143
- for (const file of files) {
144
- const filePath = path.join(this.cacheDir, file);
145
- const stats = await fs.stat(filePath);
146
- totalSize += stats.size;
147
- }
148
- return totalSize;
149
- }
150
- }
151
- /**
152
- * Create a singleton instance for convenience
153
- */
154
- export const s3Downloader = new S3Downloader();