@outputai/cli 0.1.3 → 0.1.4-dev.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 (278) hide show
  1. package/dist/api/generated/api.d.ts +820 -0
  2. package/dist/api/generated/api.js +226 -0
  3. package/dist/api/http_client.d.ts +27 -0
  4. package/dist/api/http_client.js +71 -0
  5. package/dist/api/orval_post_process.d.ts +11 -0
  6. package/dist/api/orval_post_process.js +46 -0
  7. package/dist/api/parser.d.ts +17 -0
  8. package/dist/api/parser.js +68 -0
  9. package/dist/assets/config/costs.yml +309 -0
  10. package/dist/assets/docker/docker-compose-dev.yml +146 -0
  11. package/dist/commands/credentials/edit.d.ts +10 -0
  12. package/dist/commands/credentials/edit.js +67 -0
  13. package/dist/commands/credentials/edit.spec.d.ts +1 -0
  14. package/dist/commands/credentials/edit.spec.js +73 -0
  15. package/dist/commands/credentials/get.d.ts +13 -0
  16. package/dist/commands/credentials/get.js +46 -0
  17. package/dist/commands/credentials/get.spec.d.ts +1 -0
  18. package/dist/commands/credentials/get.spec.js +74 -0
  19. package/dist/commands/credentials/init.d.ts +11 -0
  20. package/dist/commands/credentials/init.js +45 -0
  21. package/dist/commands/credentials/init.spec.d.ts +1 -0
  22. package/dist/commands/credentials/init.spec.js +68 -0
  23. package/dist/commands/credentials/show.d.ts +10 -0
  24. package/dist/commands/credentials/show.js +33 -0
  25. package/dist/commands/credentials/show.spec.d.ts +1 -0
  26. package/dist/commands/credentials/show.spec.js +57 -0
  27. package/dist/commands/dev/eject.d.ts +11 -0
  28. package/dist/commands/dev/eject.js +58 -0
  29. package/dist/commands/dev/eject.spec.d.ts +1 -0
  30. package/dist/commands/dev/eject.spec.js +109 -0
  31. package/dist/commands/dev/index.d.ts +14 -0
  32. package/dist/commands/dev/index.js +173 -0
  33. package/dist/commands/dev/index.spec.d.ts +1 -0
  34. package/dist/commands/dev/index.spec.js +239 -0
  35. package/dist/commands/init.d.ts +12 -0
  36. package/dist/commands/init.js +37 -0
  37. package/dist/commands/init.spec.d.ts +1 -0
  38. package/dist/commands/init.spec.js +100 -0
  39. package/dist/commands/update.d.ts +14 -0
  40. package/dist/commands/update.js +120 -0
  41. package/dist/commands/update.spec.d.ts +1 -0
  42. package/dist/commands/update.spec.js +178 -0
  43. package/dist/commands/workflow/cost.d.ts +16 -0
  44. package/dist/commands/workflow/cost.js +71 -0
  45. package/dist/commands/workflow/cost.spec.d.ts +1 -0
  46. package/dist/commands/workflow/cost.spec.js +47 -0
  47. package/dist/commands/workflow/dataset/generate.d.ts +22 -0
  48. package/dist/commands/workflow/dataset/generate.js +143 -0
  49. package/dist/commands/workflow/dataset/list.d.ts +12 -0
  50. package/dist/commands/workflow/dataset/list.js +87 -0
  51. package/dist/commands/workflow/debug.d.ts +16 -0
  52. package/dist/commands/workflow/debug.js +60 -0
  53. package/dist/commands/workflow/debug.spec.d.ts +1 -0
  54. package/dist/commands/workflow/debug.spec.js +34 -0
  55. package/dist/commands/workflow/generate.d.ts +17 -0
  56. package/dist/commands/workflow/generate.js +85 -0
  57. package/dist/commands/workflow/generate.spec.d.ts +1 -0
  58. package/dist/commands/workflow/generate.spec.js +115 -0
  59. package/dist/commands/workflow/list.d.ts +22 -0
  60. package/dist/commands/workflow/list.js +152 -0
  61. package/dist/commands/workflow/list.spec.d.ts +1 -0
  62. package/dist/commands/workflow/list.spec.js +99 -0
  63. package/dist/commands/workflow/plan.d.ts +12 -0
  64. package/dist/commands/workflow/plan.js +66 -0
  65. package/dist/commands/workflow/plan.spec.d.ts +1 -0
  66. package/dist/commands/workflow/plan.spec.js +341 -0
  67. package/dist/commands/workflow/reset.d.ts +14 -0
  68. package/dist/commands/workflow/reset.js +51 -0
  69. package/dist/commands/workflow/result.d.ts +13 -0
  70. package/dist/commands/workflow/result.js +46 -0
  71. package/dist/commands/workflow/result.spec.d.ts +1 -0
  72. package/dist/commands/workflow/result.spec.js +23 -0
  73. package/dist/commands/workflow/run.d.ts +16 -0
  74. package/dist/commands/workflow/run.js +97 -0
  75. package/dist/commands/workflow/run.spec.d.ts +1 -0
  76. package/dist/commands/workflow/run.spec.js +110 -0
  77. package/dist/commands/workflow/runs/list.d.ts +14 -0
  78. package/dist/commands/workflow/runs/list.js +104 -0
  79. package/dist/commands/workflow/start.d.ts +15 -0
  80. package/dist/commands/workflow/start.js +62 -0
  81. package/dist/commands/workflow/start.spec.d.ts +1 -0
  82. package/dist/commands/workflow/start.spec.js +28 -0
  83. package/dist/commands/workflow/status.d.ts +13 -0
  84. package/dist/commands/workflow/status.js +57 -0
  85. package/dist/commands/workflow/status.spec.d.ts +1 -0
  86. package/dist/commands/workflow/status.spec.js +33 -0
  87. package/dist/commands/workflow/stop.d.ts +10 -0
  88. package/dist/commands/workflow/stop.js +31 -0
  89. package/dist/commands/workflow/stop.spec.d.ts +1 -0
  90. package/dist/commands/workflow/stop.spec.js +17 -0
  91. package/dist/commands/workflow/terminate.d.ts +13 -0
  92. package/dist/commands/workflow/terminate.js +39 -0
  93. package/dist/commands/workflow/test_eval.d.ts +20 -0
  94. package/dist/commands/workflow/test_eval.js +151 -0
  95. package/dist/config.d.ts +47 -0
  96. package/dist/config.js +47 -0
  97. package/dist/generated/framework_version.json +3 -0
  98. package/dist/hooks/init.d.ts +3 -0
  99. package/dist/hooks/init.js +30 -0
  100. package/dist/hooks/init.spec.d.ts +1 -0
  101. package/dist/hooks/init.spec.js +54 -0
  102. package/dist/index.d.ts +1 -0
  103. package/dist/index.js +1 -0
  104. package/dist/index.spec.d.ts +1 -0
  105. package/dist/index.spec.js +6 -0
  106. package/dist/services/claude_client.d.ts +30 -0
  107. package/dist/services/claude_client.integration.test.d.ts +1 -0
  108. package/dist/services/claude_client.integration.test.js +43 -0
  109. package/dist/services/claude_client.js +215 -0
  110. package/dist/services/claude_client.spec.d.ts +1 -0
  111. package/dist/services/claude_client.spec.js +145 -0
  112. package/dist/services/coding_agents.d.ts +36 -0
  113. package/dist/services/coding_agents.js +236 -0
  114. package/dist/services/coding_agents.spec.d.ts +1 -0
  115. package/dist/services/coding_agents.spec.js +256 -0
  116. package/dist/services/copy_assets.spec.d.ts +1 -0
  117. package/dist/services/copy_assets.spec.js +22 -0
  118. package/dist/services/cost_calculator.d.ts +18 -0
  119. package/dist/services/cost_calculator.js +359 -0
  120. package/dist/services/cost_calculator.spec.d.ts +1 -0
  121. package/dist/services/cost_calculator.spec.js +540 -0
  122. package/dist/services/credentials_service.d.ts +12 -0
  123. package/dist/services/credentials_service.integration.test.d.ts +1 -0
  124. package/dist/services/credentials_service.integration.test.js +66 -0
  125. package/dist/services/credentials_service.js +64 -0
  126. package/dist/services/credentials_service.spec.d.ts +1 -0
  127. package/dist/services/credentials_service.spec.js +106 -0
  128. package/dist/services/datasets.d.ts +20 -0
  129. package/dist/services/datasets.js +132 -0
  130. package/dist/services/docker.d.ts +39 -0
  131. package/dist/services/docker.js +160 -0
  132. package/dist/services/docker.spec.d.ts +1 -0
  133. package/dist/services/docker.spec.js +124 -0
  134. package/dist/services/env_configurator.d.ts +15 -0
  135. package/dist/services/env_configurator.js +163 -0
  136. package/dist/services/env_configurator.spec.d.ts +1 -0
  137. package/dist/services/env_configurator.spec.js +192 -0
  138. package/dist/services/generate_plan_name@v1.prompt +24 -0
  139. package/dist/services/messages.d.ts +9 -0
  140. package/dist/services/messages.js +338 -0
  141. package/dist/services/messages.spec.d.ts +1 -0
  142. package/dist/services/messages.spec.js +55 -0
  143. package/dist/services/npm_update_service.d.ts +6 -0
  144. package/dist/services/npm_update_service.js +87 -0
  145. package/dist/services/npm_update_service.spec.d.ts +1 -0
  146. package/dist/services/npm_update_service.spec.js +104 -0
  147. package/dist/services/project_scaffold.d.ts +31 -0
  148. package/dist/services/project_scaffold.js +212 -0
  149. package/dist/services/project_scaffold.spec.d.ts +1 -0
  150. package/dist/services/project_scaffold.spec.js +122 -0
  151. package/dist/services/s3_trace_downloader.d.ts +12 -0
  152. package/dist/services/s3_trace_downloader.js +57 -0
  153. package/dist/services/template_processor.d.ts +14 -0
  154. package/dist/services/template_processor.js +57 -0
  155. package/dist/services/trace_reader.d.ts +16 -0
  156. package/dist/services/trace_reader.js +57 -0
  157. package/dist/services/trace_reader.spec.d.ts +1 -0
  158. package/dist/services/trace_reader.spec.js +78 -0
  159. package/dist/services/version_check.d.ts +6 -0
  160. package/dist/services/version_check.js +52 -0
  161. package/dist/services/version_check.spec.d.ts +1 -0
  162. package/dist/services/version_check.spec.js +106 -0
  163. package/dist/services/workflow_builder.d.ts +16 -0
  164. package/dist/services/workflow_builder.js +86 -0
  165. package/dist/services/workflow_builder.spec.d.ts +1 -0
  166. package/dist/services/workflow_builder.spec.js +165 -0
  167. package/dist/services/workflow_generator.d.ts +5 -0
  168. package/dist/services/workflow_generator.js +40 -0
  169. package/dist/services/workflow_generator.spec.d.ts +1 -0
  170. package/dist/services/workflow_generator.spec.js +77 -0
  171. package/dist/services/workflow_planner.d.ts +15 -0
  172. package/dist/services/workflow_planner.js +48 -0
  173. package/dist/services/workflow_planner.spec.d.ts +1 -0
  174. package/dist/services/workflow_planner.spec.js +122 -0
  175. package/dist/services/workflow_runs.d.ts +14 -0
  176. package/dist/services/workflow_runs.js +25 -0
  177. package/dist/templates/agent_instructions/CLAUDE.md.template +19 -0
  178. package/dist/templates/agent_instructions/dotclaude/settings.json.template +29 -0
  179. package/dist/templates/project/.env.example.template +9 -0
  180. package/dist/templates/project/.gitignore.template +35 -0
  181. package/dist/templates/project/README.md.template +100 -0
  182. package/dist/templates/project/config/costs.yml.template +29 -0
  183. package/dist/templates/project/package.json.template +25 -0
  184. package/dist/templates/project/src/clients/jina.ts.template +30 -0
  185. package/dist/templates/project/src/shared/utils/string.ts.template +3 -0
  186. package/dist/templates/project/src/shared/utils/url.ts.template +15 -0
  187. package/dist/templates/project/src/workflows/blog_evaluator/evaluators.ts.template +23 -0
  188. package/dist/templates/project/src/workflows/blog_evaluator/prompts/signal_noise@v1.prompt.template +26 -0
  189. package/dist/templates/project/src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json.template +3 -0
  190. package/dist/templates/project/src/workflows/blog_evaluator/steps.ts.template +27 -0
  191. package/dist/templates/project/src/workflows/blog_evaluator/types.ts.template +30 -0
  192. package/dist/templates/project/src/workflows/blog_evaluator/utils.ts.template +15 -0
  193. package/dist/templates/project/src/workflows/blog_evaluator/workflow.ts.template +27 -0
  194. package/dist/templates/project/tsconfig.json.template +20 -0
  195. package/dist/templates/workflow/README.md.template +216 -0
  196. package/dist/templates/workflow/evaluators.ts.template +21 -0
  197. package/dist/templates/workflow/prompts/example@v1.prompt.template +15 -0
  198. package/dist/templates/workflow/scenarios/test_input.json.template +3 -0
  199. package/dist/templates/workflow/steps.ts.template +20 -0
  200. package/dist/templates/workflow/types.ts.template +13 -0
  201. package/dist/templates/workflow/workflow.ts.template +23 -0
  202. package/dist/test_helpers/mocks.d.ts +38 -0
  203. package/dist/test_helpers/mocks.js +77 -0
  204. package/dist/types/cost.d.ts +149 -0
  205. package/dist/types/cost.js +6 -0
  206. package/dist/types/domain.d.ts +20 -0
  207. package/dist/types/domain.js +4 -0
  208. package/dist/types/errors.d.ts +68 -0
  209. package/dist/types/errors.js +100 -0
  210. package/dist/types/errors.spec.d.ts +1 -0
  211. package/dist/types/errors.spec.js +18 -0
  212. package/dist/types/generator.d.ts +26 -0
  213. package/dist/types/generator.js +1 -0
  214. package/dist/types/trace.d.ts +161 -0
  215. package/dist/types/trace.js +18 -0
  216. package/dist/utils/claude.d.ts +5 -0
  217. package/dist/utils/claude.js +19 -0
  218. package/dist/utils/claude.spec.d.ts +1 -0
  219. package/dist/utils/claude.spec.js +119 -0
  220. package/dist/utils/constants.d.ts +5 -0
  221. package/dist/utils/constants.js +4 -0
  222. package/dist/utils/cost_formatter.d.ts +5 -0
  223. package/dist/utils/cost_formatter.js +218 -0
  224. package/dist/utils/date_formatter.d.ts +23 -0
  225. package/dist/utils/date_formatter.js +49 -0
  226. package/dist/utils/env_loader.d.ts +1 -0
  227. package/dist/utils/env_loader.js +22 -0
  228. package/dist/utils/env_loader.spec.d.ts +1 -0
  229. package/dist/utils/env_loader.spec.js +43 -0
  230. package/dist/utils/error_handler.d.ts +8 -0
  231. package/dist/utils/error_handler.js +71 -0
  232. package/dist/utils/error_utils.d.ts +24 -0
  233. package/dist/utils/error_utils.js +87 -0
  234. package/dist/utils/file_system.d.ts +3 -0
  235. package/dist/utils/file_system.js +33 -0
  236. package/dist/utils/format_workflow_result.d.ts +5 -0
  237. package/dist/utils/format_workflow_result.js +18 -0
  238. package/dist/utils/format_workflow_result.spec.d.ts +1 -0
  239. package/dist/utils/format_workflow_result.spec.js +81 -0
  240. package/dist/utils/framework_version.d.ts +4 -0
  241. package/dist/utils/framework_version.js +4 -0
  242. package/dist/utils/framework_version.spec.d.ts +1 -0
  243. package/dist/utils/framework_version.spec.js +13 -0
  244. package/dist/utils/header_utils.d.ts +12 -0
  245. package/dist/utils/header_utils.js +29 -0
  246. package/dist/utils/header_utils.spec.d.ts +1 -0
  247. package/dist/utils/header_utils.spec.js +52 -0
  248. package/dist/utils/input_parser.d.ts +1 -0
  249. package/dist/utils/input_parser.js +19 -0
  250. package/dist/utils/output_formatter.d.ts +2 -0
  251. package/dist/utils/output_formatter.js +11 -0
  252. package/dist/utils/paths.d.ts +25 -0
  253. package/dist/utils/paths.js +36 -0
  254. package/dist/utils/process.d.ts +4 -0
  255. package/dist/utils/process.js +50 -0
  256. package/dist/utils/resolve_input.d.ts +1 -0
  257. package/dist/utils/resolve_input.js +22 -0
  258. package/dist/utils/scenario_resolver.d.ts +9 -0
  259. package/dist/utils/scenario_resolver.js +93 -0
  260. package/dist/utils/scenario_resolver.spec.d.ts +1 -0
  261. package/dist/utils/scenario_resolver.spec.js +214 -0
  262. package/dist/utils/secret_sanitizer.d.ts +1 -0
  263. package/dist/utils/secret_sanitizer.js +29 -0
  264. package/dist/utils/sleep.d.ts +5 -0
  265. package/dist/utils/sleep.js +5 -0
  266. package/dist/utils/template.d.ts +9 -0
  267. package/dist/utils/template.js +30 -0
  268. package/dist/utils/template.spec.d.ts +1 -0
  269. package/dist/utils/template.spec.js +77 -0
  270. package/dist/utils/trace_extractor.d.ts +27 -0
  271. package/dist/utils/trace_extractor.js +53 -0
  272. package/dist/utils/trace_formatter.d.ts +11 -0
  273. package/dist/utils/trace_formatter.js +402 -0
  274. package/dist/utils/validation.d.ts +13 -0
  275. package/dist/utils/validation.js +25 -0
  276. package/dist/utils/validation.spec.d.ts +1 -0
  277. package/dist/utils/validation.spec.js +140 -0
  278. package/package.json +4 -4
@@ -0,0 +1,87 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import Table from 'cli-table3';
3
+ import { listDatasets } from '#services/datasets.js';
4
+ const OutputFormat = {
5
+ TABLE: 'table',
6
+ JSON: 'json',
7
+ TEXT: 'text'
8
+ };
9
+ function createDatasetsTable(datasets) {
10
+ const table = new Table({
11
+ head: ['Name', 'Output Date', 'Eval Date', 'Has Output', 'Path'],
12
+ colWidths: [25, 22, 22, 16, 50],
13
+ wordWrap: true,
14
+ style: {
15
+ head: ['cyan']
16
+ }
17
+ });
18
+ datasets.forEach(dataset => {
19
+ table.push([
20
+ dataset.name,
21
+ dataset.lastOutputDate ?? '-',
22
+ dataset.lastEvalDate ?? '-',
23
+ dataset.hasLastOutput ? 'Yes' : 'No',
24
+ dataset.path
25
+ ]);
26
+ });
27
+ return table.toString();
28
+ }
29
+ function formatDatasetsAsText(datasets) {
30
+ if (datasets.length === 0) {
31
+ return 'No datasets found.';
32
+ }
33
+ return datasets.map(dataset => {
34
+ const cached = dataset.hasLastOutput ? '(cached)' : '(no output)';
35
+ const outputDate = dataset.lastOutputDate ? ` [output: ${dataset.lastOutputDate}]` : '';
36
+ const evalDate = dataset.lastEvalDate ? ` [eval: ${dataset.lastEvalDate}]` : '';
37
+ return `${dataset.name} ${cached}${outputDate}${evalDate}`;
38
+ }).join('\n');
39
+ }
40
+ function formatDatasetsAsJson(datasets) {
41
+ return JSON.stringify(datasets, null, 2);
42
+ }
43
+ function formatDatasets(datasets, format) {
44
+ if (format === OutputFormat.JSON) {
45
+ return formatDatasetsAsJson(datasets);
46
+ }
47
+ if (format === OutputFormat.TABLE) {
48
+ return createDatasetsTable(datasets);
49
+ }
50
+ return formatDatasetsAsText(datasets);
51
+ }
52
+ export default class DatasetList extends Command {
53
+ static description = 'List datasets for a workflow';
54
+ static examples = [
55
+ '<%= config.bin %> <%= command.id %> simple',
56
+ '<%= config.bin %> <%= command.id %> simple --format json',
57
+ '<%= config.bin %> <%= command.id %> simple --format table'
58
+ ];
59
+ static args = {
60
+ workflowName: Args.string({
61
+ description: 'Workflow name to list datasets for',
62
+ required: true
63
+ })
64
+ };
65
+ static flags = {
66
+ format: Flags.string({
67
+ char: 'f',
68
+ description: 'Output format',
69
+ options: [OutputFormat.TABLE, OutputFormat.JSON, OutputFormat.TEXT],
70
+ default: OutputFormat.TABLE
71
+ })
72
+ };
73
+ async run() {
74
+ const { args, flags } = await this.parse(DatasetList);
75
+ const datasets = await listDatasets(args.workflowName);
76
+ if (datasets.length === 0) {
77
+ this.log(`No datasets found for workflow "${args.workflowName}".`);
78
+ this.log('Generate datasets with: output workflow dataset generate');
79
+ return;
80
+ }
81
+ const output = formatDatasets(datasets, flags.format);
82
+ this.log(output);
83
+ if (flags.format !== OutputFormat.JSON) {
84
+ this.log(`\nFound ${datasets.length} dataset(s) for "${args.workflowName}"`);
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class WorkflowDebug extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ workflowId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ private conditionalLog;
13
+ private outputJson;
14
+ private displayTextTrace;
15
+ catch(error: Error): Promise<void>;
16
+ }
@@ -0,0 +1,60 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { OUTPUT_FORMAT } from '#utils/constants.js';
3
+ import { displayDebugTree } from '#utils/trace_formatter.js';
4
+ import { getTrace } from '#services/trace_reader.js';
5
+ import { handleApiError } from '#utils/error_handler.js';
6
+ export default class WorkflowDebug extends Command {
7
+ static description = 'Get and display workflow execution trace for debugging';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> wf-12345',
10
+ '<%= config.bin %> <%= command.id %> wf-12345 --format json',
11
+ '<%= config.bin %> <%= command.id %> wf-12345 --format text'
12
+ ];
13
+ static args = {
14
+ workflowId: Args.string({
15
+ description: 'The workflow ID to debug',
16
+ required: true
17
+ })
18
+ };
19
+ static flags = {
20
+ format: Flags.string({
21
+ char: 'f',
22
+ description: 'Output format',
23
+ options: [OUTPUT_FORMAT.JSON, OUTPUT_FORMAT.TEXT],
24
+ default: OUTPUT_FORMAT.TEXT
25
+ })
26
+ };
27
+ async run() {
28
+ const { args, flags } = await this.parse(WorkflowDebug);
29
+ const isJsonFormat = flags.format === OUTPUT_FORMAT.JSON;
30
+ this.conditionalLog(`Fetching debug information for workflow: ${args.workflowId}...`, isJsonFormat);
31
+ const { data: traceData, location } = await getTrace(args.workflowId);
32
+ const source = location.isRemote ? 'remote' : 'local';
33
+ this.conditionalLog(`Trace source: ${source}${!location.isRemote ? ` (${location.path})` : ''}`, isJsonFormat);
34
+ if (isJsonFormat) {
35
+ this.outputJson(traceData);
36
+ return;
37
+ }
38
+ this.displayTextTrace(traceData);
39
+ }
40
+ conditionalLog(message, disabled) {
41
+ if (!disabled) {
42
+ this.log(message);
43
+ }
44
+ }
45
+ outputJson(data) {
46
+ this.log(JSON.stringify(data, null, 2));
47
+ }
48
+ displayTextTrace(traceData) {
49
+ this.log('\nTrace Log:');
50
+ this.log('─'.repeat(80));
51
+ this.log(displayDebugTree(traceData));
52
+ this.log('\n' + '─'.repeat(80));
53
+ this.log('Tip: Use --format json for the full untruncated trace');
54
+ }
55
+ async catch(error) {
56
+ return handleApiError(error, (...args) => this.error(...args), {
57
+ 404: 'Workflow not found or trace not available. Check the workflow ID.'
58
+ });
59
+ }
60
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ // Mock the TraceReader service
3
+ vi.mock('../../services/trace_reader.js', () => ({
4
+ getTrace: vi.fn()
5
+ }));
6
+ // Mock the utilities
7
+ vi.mock('../../utils/trace_formatter.js', () => ({
8
+ displayDebugTree: vi.fn()
9
+ }));
10
+ describe('workflow debug command', () => {
11
+ beforeEach(() => {
12
+ vi.clearAllMocks();
13
+ });
14
+ describe('command definition', () => {
15
+ it('should export a valid OCLIF command', async () => {
16
+ const WorkflowDebug = (await import('./debug.js')).default;
17
+ expect(WorkflowDebug).toBeDefined();
18
+ expect(WorkflowDebug.description).toContain('Get and display workflow execution trace for debugging');
19
+ expect(WorkflowDebug.args).toHaveProperty('workflowId');
20
+ expect(WorkflowDebug.flags).toHaveProperty('format');
21
+ });
22
+ it('should have correct flag configuration', async () => {
23
+ const WorkflowDebug = (await import('./debug.js')).default;
24
+ // Format flag
25
+ expect(WorkflowDebug.flags.format.options).toEqual(['json', 'text']);
26
+ expect(WorkflowDebug.flags.format.default).toBe('text');
27
+ });
28
+ it('should have correct examples', async () => {
29
+ const WorkflowDebug = (await import('./debug.js')).default;
30
+ expect(WorkflowDebug.examples).toBeDefined();
31
+ expect(WorkflowDebug.examples.length).toBeGreaterThan(0);
32
+ });
33
+ });
34
+ });
@@ -0,0 +1,17 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Generate extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ skeleton: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'output-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ 'plan-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ static args: {
13
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
14
+ };
15
+ run(): Promise<void>;
16
+ private displaySuccess;
17
+ }
@@ -0,0 +1,85 @@
1
+ import { Args, Command, Flags, ux } from '@oclif/core';
2
+ import { generateWorkflow } from '#services/workflow_generator.js';
3
+ import { buildWorkflow, buildWorkflowInteractiveLoop } from '#services/workflow_builder.js';
4
+ import { ensureOutputAISystem } from '#services/coding_agents.js';
5
+ import { getWorkflowGenerateSuccessMessage } from '#services/messages.js';
6
+ import { DEFAULT_OUTPUT_DIRS } from '#utils/paths.js';
7
+ import path from 'node:path';
8
+ export default class Generate extends Command {
9
+ static description = 'Generate a new Output workflow from a skeleton or plan file';
10
+ static examples = [
11
+ '<%= config.bin %> <%= command.id %> my-workflow --skeleton',
12
+ '<%= config.bin %> <%= command.id %> my-workflow --skeleton --description "Process and transform data"',
13
+ '<%= config.bin %> <%= command.id %> my-workflow --plan-file .outputai/plans/2025_10_09_my_workflow/PLAN.md',
14
+ '<%= config.bin %> <%= command.id %> my-workflow --skeleton --output-dir ./custom/path'
15
+ ];
16
+ static flags = {
17
+ skeleton: Flags.boolean({
18
+ char: 's',
19
+ description: 'Generate minimal skeleton workflow without example steps',
20
+ default: false
21
+ }),
22
+ description: Flags.string({
23
+ char: 'd',
24
+ description: 'Description of the workflow',
25
+ required: false
26
+ }),
27
+ 'output-dir': Flags.string({
28
+ char: 'o',
29
+ description: 'Output directory for the workflow',
30
+ default: DEFAULT_OUTPUT_DIRS.workflows
31
+ }),
32
+ force: Flags.boolean({
33
+ char: 'f',
34
+ description: 'Overwrite existing directory',
35
+ default: false
36
+ }),
37
+ 'plan-file': Flags.string({
38
+ char: 'p',
39
+ description: 'Path to plan file for AI-assisted workflow implementation',
40
+ required: false
41
+ })
42
+ };
43
+ static args = {
44
+ name: Args.string({
45
+ required: true,
46
+ description: 'Name of the workflow to generate'
47
+ })
48
+ };
49
+ async run() {
50
+ const { args, flags } = await this.parse(Generate);
51
+ const planFile = flags['plan-file'];
52
+ if (!flags.skeleton && !planFile) {
53
+ this.error('Full workflow generation not implemented yet. Please use --skeleton flag or --plan-file');
54
+ }
55
+ try {
56
+ const result = await generateWorkflow({
57
+ name: args.name,
58
+ description: flags.description,
59
+ outputDir: flags['output-dir'],
60
+ skeleton: flags.skeleton,
61
+ force: flags.force
62
+ });
63
+ if (planFile) {
64
+ this.log('\nStarting AI-assisted workflow implementation...\n');
65
+ const projectRoot = process.cwd();
66
+ await ensureOutputAISystem(projectRoot);
67
+ const absolutePlanPath = path.resolve(projectRoot, planFile);
68
+ const buildOutput = await buildWorkflow(absolutePlanPath, result.targetDir, args.name);
69
+ await buildWorkflowInteractiveLoop(buildOutput);
70
+ this.log(ux.colorize('green', '\nWorkflow implementation complete!\n'));
71
+ }
72
+ this.displaySuccess(result);
73
+ }
74
+ catch (error) {
75
+ if (error instanceof Error) {
76
+ this.error(error.message);
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+ displaySuccess(result) {
82
+ const message = getWorkflowGenerateSuccessMessage(result.workflowName, result.targetDir, result.filesCreated);
83
+ this.log(message);
84
+ }
85
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,115 @@
1
+ /* eslint-disable no-restricted-syntax, @typescript-eslint/no-explicit-any, init-declarations */
2
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
3
+ import Generate from './generate.js';
4
+ import { generateWorkflow } from '#services/workflow_generator.js';
5
+ import { InvalidNameError, WorkflowExistsError } from '#types/errors.js';
6
+ vi.mock('../../services/workflow_generator.js');
7
+ describe('Generate Command', () => {
8
+ let mockGenerateWorkflow;
9
+ let logSpy;
10
+ const createCommand = () => {
11
+ const cmd = new Generate([], {});
12
+ cmd.log = vi.fn();
13
+ cmd.error = vi.fn((message) => {
14
+ throw new Error(message);
15
+ });
16
+ cmd.parse = vi.fn();
17
+ logSpy = cmd.log;
18
+ return cmd;
19
+ };
20
+ beforeEach(() => {
21
+ vi.clearAllMocks();
22
+ mockGenerateWorkflow = vi.mocked(generateWorkflow);
23
+ });
24
+ describe('successful workflow generation', () => {
25
+ it('should generate workflow with skeleton flag', async () => {
26
+ const cmd = createCommand();
27
+ cmd.parse.mockResolvedValue({
28
+ args: { name: 'test-workflow' },
29
+ flags: {
30
+ skeleton: true,
31
+ description: 'Test description',
32
+ 'output-dir': '/tmp',
33
+ force: false
34
+ }
35
+ });
36
+ mockGenerateWorkflow.mockResolvedValue({
37
+ workflowName: 'test-workflow',
38
+ targetDir: '/tmp/test-workflow',
39
+ filesCreated: ['index.ts', 'steps.ts', 'types.ts']
40
+ });
41
+ await cmd.run();
42
+ expect(mockGenerateWorkflow).toHaveBeenCalledWith({
43
+ name: 'test-workflow',
44
+ description: 'Test description',
45
+ outputDir: '/tmp',
46
+ skeleton: true,
47
+ force: false
48
+ });
49
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('SUCCESS!'));
50
+ expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('test-workflow'));
51
+ });
52
+ it('should require skeleton flag and reject without it', async () => {
53
+ const cmd = createCommand();
54
+ cmd.parse.mockResolvedValue({
55
+ args: { name: 'test-workflow' },
56
+ flags: {
57
+ skeleton: false,
58
+ 'output-dir': '/tmp',
59
+ force: false
60
+ }
61
+ });
62
+ await expect(cmd.run()).rejects.toThrow('Full workflow generation not implemented yet. Please use --skeleton flag');
63
+ expect(mockGenerateWorkflow).not.toHaveBeenCalled();
64
+ });
65
+ });
66
+ describe('error handling', () => {
67
+ it('should handle invalid name error', async () => {
68
+ const cmd = createCommand();
69
+ cmd.parse.mockResolvedValue({
70
+ args: { name: 'invalid name' },
71
+ flags: { 'output-dir': '/tmp', skeleton: true, force: false }
72
+ });
73
+ mockGenerateWorkflow.mockRejectedValue(new InvalidNameError('invalid name'));
74
+ await expect(cmd.run()).rejects.toThrow(/Invalid workflow name/i);
75
+ });
76
+ it('should handle workflow exists error', async () => {
77
+ const cmd = createCommand();
78
+ cmd.parse.mockResolvedValue({
79
+ args: { name: 'existing-workflow' },
80
+ flags: { 'output-dir': '/tmp', skeleton: true, force: false }
81
+ });
82
+ mockGenerateWorkflow.mockRejectedValue(new WorkflowExistsError('existing-workflow', '/tmp/existing-workflow'));
83
+ await expect(cmd.run()).rejects.toThrow(/already exists/i);
84
+ });
85
+ it('should re-throw non-CLI errors', async () => {
86
+ const cmd = createCommand();
87
+ cmd.parse.mockResolvedValue({
88
+ args: { name: 'test-workflow' },
89
+ flags: { 'output-dir': '/tmp', skeleton: true, force: false }
90
+ });
91
+ const systemError = new Error('System error');
92
+ mockGenerateWorkflow.mockRejectedValue(systemError);
93
+ await expect(cmd.run()).rejects.toThrow(systemError);
94
+ });
95
+ });
96
+ describe('success display', () => {
97
+ it('should display correct success message and next steps', async () => {
98
+ const cmd = createCommand();
99
+ cmd.parse.mockResolvedValue({
100
+ args: { name: 'my-workflow' },
101
+ flags: { 'output-dir': '/custom/path', skeleton: true, force: false }
102
+ });
103
+ mockGenerateWorkflow.mockResolvedValue({
104
+ workflowName: 'my-workflow',
105
+ targetDir: '/custom/path/my-workflow',
106
+ filesCreated: ['index.ts', 'steps.ts', 'types.ts']
107
+ });
108
+ await cmd.run();
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'));
113
+ });
114
+ });
115
+ });
@@ -0,0 +1,22 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type Workflow } from '#api/generated/api.js';
3
+ interface WorkflowDisplay {
4
+ name: string;
5
+ description: string;
6
+ inputs: string;
7
+ outputs: string;
8
+ scenarios: string;
9
+ }
10
+ export declare function parseWorkflowForDisplay(workflow: Workflow): WorkflowDisplay;
11
+ export default class WorkflowList extends Command {
12
+ static description: string;
13
+ static examples: string[];
14
+ static flags: {
15
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
16
+ detailed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ filter: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ };
19
+ run(): Promise<void>;
20
+ catch(error: Error): Promise<void>;
21
+ }
22
+ export {};
@@ -0,0 +1,152 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import Table from 'cli-table3';
3
+ import { getWorkflowCatalog } from '#api/generated/api.js';
4
+ import { parseWorkflowDefinition, formatParameters } from '#api/parser.js';
5
+ import { handleApiError } from '#utils/error_handler.js';
6
+ import { listScenariosForWorkflow } from '#utils/scenario_resolver.js';
7
+ const OUTPUT_FORMAT = {
8
+ LIST: 'list',
9
+ TABLE: 'table',
10
+ JSON: 'json'
11
+ };
12
+ export function parseWorkflowForDisplay(workflow) {
13
+ const parsed = parseWorkflowDefinition(workflow);
14
+ const scenarioNames = listScenariosForWorkflow(workflow.name, workflow.path);
15
+ return {
16
+ name: parsed.name,
17
+ description: parsed.description || 'No description',
18
+ inputs: formatParameters(parsed.inputs),
19
+ outputs: formatParameters(parsed.outputs),
20
+ scenarios: scenarioNames.length > 0 ? scenarioNames.join(', ') : 'none'
21
+ };
22
+ }
23
+ function caseInsensitiveIncludes(str, filter) {
24
+ return str.toLowerCase().includes(filter.toLowerCase());
25
+ }
26
+ function matchName(filterString) {
27
+ return workflow => {
28
+ const name = workflow.name || '';
29
+ return caseInsensitiveIncludes(name, filterString);
30
+ };
31
+ }
32
+ function sortWorkflowsByName(workflows) {
33
+ return [...workflows].sort((a, b) => {
34
+ const nameA = (a.name || '').toLowerCase();
35
+ const nameB = (b.name || '').toLowerCase();
36
+ return nameA.localeCompare(nameB);
37
+ });
38
+ }
39
+ function createWorkflowTable(workflows, detailed) {
40
+ const table = new Table({
41
+ head: ['Name', 'Description', 'Inputs', 'Outputs', 'Scenarios'],
42
+ colWidths: detailed ? [30, 42, 42, 42, 60] : [24, 30, 24, 24, 48],
43
+ wordWrap: true,
44
+ style: {
45
+ head: ['cyan']
46
+ }
47
+ });
48
+ const sortedWorkflows = sortWorkflowsByName(workflows);
49
+ sortedWorkflows.forEach(workflow => {
50
+ const display = parseWorkflowForDisplay(workflow);
51
+ if (detailed) {
52
+ const inputs = display.inputs.split(', ').join('\n');
53
+ const outputs = display.outputs.split(', ').join('\n');
54
+ const scenarios = display.scenarios.split(', ').join('\n');
55
+ table.push([display.name, display.description, inputs, outputs, scenarios]);
56
+ }
57
+ else {
58
+ table.push([display.name, display.description, display.inputs, display.outputs, display.scenarios]);
59
+ }
60
+ });
61
+ return table.toString();
62
+ }
63
+ function formatWorkflowsAsList(workflows) {
64
+ const sortedWorkflows = sortWorkflowsByName(workflows);
65
+ const names = sortedWorkflows.map(w => parseWorkflowForDisplay(w).name);
66
+ return `\nWorkflows:\n\n${names.map(name => `- ${name}`).join('\n')}`;
67
+ }
68
+ function formatWorkflowsAsJson(workflows) {
69
+ const output = {
70
+ workflows: workflows.map(w => {
71
+ const display = parseWorkflowForDisplay(w);
72
+ return {
73
+ name: display.name,
74
+ description: display.description,
75
+ inputs: display.inputs.split(', '),
76
+ outputs: display.outputs.split(', '),
77
+ scenarios: display.scenarios === 'none' ? [] : display.scenarios.split(', '),
78
+ raw: w
79
+ };
80
+ })
81
+ };
82
+ return JSON.stringify(output, null, 2);
83
+ }
84
+ function formatWorkflows(workflows, format, detailed) {
85
+ if (format === OUTPUT_FORMAT.JSON) {
86
+ return formatWorkflowsAsJson(workflows);
87
+ }
88
+ if (format === OUTPUT_FORMAT.TABLE) {
89
+ return createWorkflowTable(workflows, detailed);
90
+ }
91
+ return formatWorkflowsAsList(workflows);
92
+ }
93
+ export default class WorkflowList extends Command {
94
+ static description = 'List available workflows from the catalog';
95
+ static examples = [
96
+ '<%= config.bin %> <%= command.id %>',
97
+ '<%= config.bin %> <%= command.id %> --format table',
98
+ '<%= config.bin %> <%= command.id %> --format json',
99
+ '<%= config.bin %> <%= command.id %> --detailed',
100
+ '<%= config.bin %> <%= command.id %> --filter simple'
101
+ ];
102
+ static flags = {
103
+ format: Flags.string({
104
+ char: 'f',
105
+ description: 'Output format',
106
+ options: [OUTPUT_FORMAT.LIST, OUTPUT_FORMAT.TABLE, OUTPUT_FORMAT.JSON],
107
+ default: OUTPUT_FORMAT.LIST
108
+ }),
109
+ detailed: Flags.boolean({
110
+ char: 'd',
111
+ description: 'Show detailed parameter information',
112
+ default: false
113
+ }),
114
+ filter: Flags.string({
115
+ description: 'Filter workflows by name'
116
+ })
117
+ };
118
+ async run() {
119
+ const { flags } = await this.parse(WorkflowList);
120
+ this.log('Fetching workflow catalog...');
121
+ const response = await getWorkflowCatalog();
122
+ if (!response) {
123
+ this.error('Failed to connect to API server. Is it running?', { exit: 1 });
124
+ }
125
+ if (!response.data) {
126
+ this.error('API returned invalid response (missing data)', { exit: 1 });
127
+ }
128
+ const data = response.data;
129
+ if (!data.workflows) {
130
+ this.error('API returned invalid response (missing workflows)', { exit: 1 });
131
+ }
132
+ if (data.workflows.length === 0) {
133
+ this.log('No workflows found in catalog.');
134
+ return;
135
+ }
136
+ const workflows = flags.filter ?
137
+ data.workflows.filter(matchName(flags.filter)) :
138
+ data.workflows;
139
+ if (workflows.length === 0 && flags.filter) {
140
+ this.log(`No workflows matching filter: ${flags.filter}`);
141
+ return;
142
+ }
143
+ const output = formatWorkflows(workflows, flags.format, flags.detailed);
144
+ this.log(output);
145
+ this.log(`\nFound ${workflows.length} workflow(s)`);
146
+ }
147
+ async catch(error) {
148
+ return handleApiError(error, (...args) => this.error(...args), {
149
+ 404: 'Catalog not found.'
150
+ });
151
+ }
152
+ }
@@ -0,0 +1 @@
1
+ export {};