@output.ai/cli 0.0.5 → 0.0.7

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 (49) hide show
  1. package/README.md +2 -3
  2. package/dist/api/http_client.js +1 -1
  3. package/dist/commands/agents/init.d.ts +2 -2
  4. package/dist/commands/agents/init.js +2 -2
  5. package/dist/commands/agents/init.spec.js +2 -2
  6. package/dist/commands/workflow/generate.d.ts +6 -5
  7. package/dist/commands/workflow/generate.js +25 -6
  8. package/dist/commands/workflow/generate.spec.js +2 -2
  9. package/dist/commands/workflow/list.d.ts +4 -4
  10. package/dist/commands/workflow/list.js +3 -3
  11. package/dist/commands/workflow/output.d.ts +2 -2
  12. package/dist/commands/workflow/output.js +4 -4
  13. package/dist/commands/workflow/plan.d.ts +2 -2
  14. package/dist/commands/workflow/plan.js +4 -3
  15. package/dist/commands/workflow/plan.spec.js +6 -4
  16. package/dist/commands/workflow/run.d.ts +4 -4
  17. package/dist/commands/workflow/run.js +5 -5
  18. package/dist/commands/workflow/start.d.ts +3 -3
  19. package/dist/commands/workflow/start.js +3 -3
  20. package/dist/commands/workflow/status.d.ts +2 -2
  21. package/dist/commands/workflow/status.js +4 -4
  22. package/dist/commands/workflow/stop.d.ts +1 -1
  23. package/dist/commands/workflow/stop.js +2 -2
  24. package/dist/services/claude_client.d.ts +18 -1
  25. package/dist/services/claude_client.js +108 -31
  26. package/dist/services/coding_agents.d.ts +7 -0
  27. package/dist/services/coding_agents.js +67 -10
  28. package/dist/services/coding_agents.spec.js +155 -14
  29. package/dist/services/template_processor.d.ts +1 -1
  30. package/dist/services/template_processor.js +1 -1
  31. package/dist/services/workflow_builder.d.ts +16 -0
  32. package/dist/services/workflow_builder.js +85 -0
  33. package/dist/services/workflow_builder.spec.d.ts +1 -0
  34. package/dist/services/workflow_builder.spec.js +165 -0
  35. package/dist/services/workflow_generator.d.ts +1 -1
  36. package/dist/services/workflow_generator.js +4 -4
  37. package/dist/services/workflow_planner.d.ts +0 -5
  38. package/dist/services/workflow_planner.js +2 -39
  39. package/dist/services/workflow_planner.spec.js +2 -77
  40. package/dist/templates/agent_instructions/commands/build_workflow.md.template +246 -0
  41. package/dist/templates/workflow/steps.ts.template +25 -54
  42. package/dist/templates/workflow/workflow.ts.template +23 -49
  43. package/dist/types/domain.d.ts +20 -0
  44. package/dist/types/domain.js +4 -0
  45. package/dist/utils/error_handler.js +1 -1
  46. package/dist/utils/paths.d.ts +1 -1
  47. package/dist/utils/paths.js +1 -1
  48. package/dist/utils/validation.js +1 -1
  49. package/package.json +7 -3
package/README.md CHANGED
@@ -67,7 +67,7 @@ output workflow generate my-workflow --force
67
67
 
68
68
  - `--description, -d` - Description of the workflow
69
69
  - `--skeleton, -s` - Generate minimal skeleton without example steps
70
- - `--output-dir, -o` - Output directory (default: `output-workflows/src`)
70
+ - `--output-dir, -o` - Output directory (default: `src/`)
71
71
  - `--force, -f` - Overwrite existing directory
72
72
 
73
73
  #### Generated Structure
@@ -76,9 +76,8 @@ The CLI creates a complete workflow structure:
76
76
 
77
77
  ```
78
78
  my-workflow/
79
- ├── index.ts # Main workflow definition
79
+ ├── workflow.ts # Main workflow definition
80
80
  ├── steps.ts # Activity/step implementations
81
- ├── types.ts # TypeScript interfaces
82
81
  ├── prompt@v1.prompt # LLM prompt template (if not skeleton)
83
82
  └── README.md # Workflow documentation
84
83
  ```
@@ -2,7 +2,7 @@
2
2
  * Custom ky-based HTTP client for Orval-generated API
3
3
  */
4
4
  import ky from 'ky';
5
- import { config } from '../config.js';
5
+ import { config } from '#config.js';
6
6
  const api = ky.create({
7
7
  prefixUrl: config.apiUrl,
8
8
  timeout: config.requestTimeout,
@@ -4,8 +4,8 @@ export default class Init extends Command {
4
4
  static examples: string[];
5
5
  static args: {};
6
6
  static flags: {
7
- 'agent-provider': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
- force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
+ 'agent-provider': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
9
  };
10
10
  run(): Promise<void>;
11
11
  }
@@ -1,6 +1,6 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { AGENT_CONFIG_DIR } from '../../config.js';
3
- import { initializeAgentConfig, AGENT_CONFIGS } from '../../services/coding_agents.js';
2
+ import { AGENT_CONFIG_DIR } from '#config.js';
3
+ import { initializeAgentConfig, AGENT_CONFIGS } from '#services/coding_agents.js';
4
4
  export default class Init extends Command {
5
5
  static description = 'Initialize agent configuration files for AI assistant integration';
6
6
  static examples = [
@@ -3,8 +3,8 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
3
3
  import fs from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import Init from './init.js';
6
- import { getTemplateDir } from '../../utils/paths.js';
7
- import { processTemplate } from '../../utils/template.js';
6
+ import { getTemplateDir } from '#utils/paths.js';
7
+ import { processTemplate } from '#utils/template.js';
8
8
  vi.mock('node:fs/promises');
9
9
  vi.mock('node:path', async () => {
10
10
  const actual = await vi.importActual('node:path');
@@ -3,13 +3,14 @@ export default class Generate extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
- skeleton: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
- description: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
- 'output-dir': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
- force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
6
+ skeleton: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ 'output-dir': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ 'plan-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
11
  };
11
12
  static args: {
12
- name: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
13
+ name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
13
14
  };
14
15
  run(): Promise<void>;
15
16
  private displaySuccess;
@@ -1,12 +1,16 @@
1
- import { Args, Command, Flags } from '@oclif/core';
2
- import { generateWorkflow } from '../../services/workflow_generator.js';
3
- import { DEFAULT_OUTPUT_DIRS } from '../../utils/paths.js';
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 { ensureOutputAIStructure } from '#services/coding_agents.js';
5
+ import { DEFAULT_OUTPUT_DIRS } from '#utils/paths.js';
6
+ import path from 'node:path';
4
7
  export default class Generate extends Command {
5
8
  static description = 'Generate a new Output SDK workflow';
6
9
  static examples = [
7
10
  '<%= config.bin %> <%= command.id %> my-workflow',
8
11
  '<%= config.bin %> <%= command.id %> my-workflow --skeleton',
9
- '<%= config.bin %> <%= command.id %> data-processing --description "Process and transform data"'
12
+ '<%= config.bin %> <%= command.id %> data-processing --description "Process and transform data"',
13
+ '<%= config.bin %> <%= command.id %> my-workflow --plan-file .outputai/plans/2025_10_09_my_workflow/PLAN.md'
10
14
  ];
11
15
  static flags = {
12
16
  skeleton: Flags.boolean({
@@ -28,6 +32,11 @@ export default class Generate extends Command {
28
32
  char: 'f',
29
33
  description: 'Overwrite existing directory',
30
34
  default: false
35
+ }),
36
+ 'plan-file': Flags.string({
37
+ char: 'p',
38
+ description: 'Path to plan file for AI-assisted workflow implementation',
39
+ required: false
31
40
  })
32
41
  };
33
42
  static args = {
@@ -38,8 +47,9 @@ export default class Generate extends Command {
38
47
  };
39
48
  async run() {
40
49
  const { args, flags } = await this.parse(Generate);
41
- if (!flags.skeleton) {
42
- this.error('Full workflow generation not implemented yet. Please use --skeleton flag');
50
+ const planFile = flags['plan-file'];
51
+ if (!flags.skeleton && !planFile) {
52
+ this.error('Full workflow generation not implemented yet. Please use --skeleton flag or --plan-file');
43
53
  }
44
54
  try {
45
55
  const result = await generateWorkflow({
@@ -49,6 +59,15 @@ export default class Generate extends Command {
49
59
  skeleton: flags.skeleton,
50
60
  force: flags.force
51
61
  });
62
+ if (planFile) {
63
+ this.log('\nStarting AI-assisted workflow implementation...\n');
64
+ const projectRoot = process.cwd();
65
+ await ensureOutputAIStructure(projectRoot);
66
+ const absolutePlanPath = path.resolve(projectRoot, planFile);
67
+ const buildOutput = await buildWorkflow(absolutePlanPath, result.targetDir, args.name);
68
+ await buildWorkflowInteractiveLoop(buildOutput);
69
+ this.log(ux.colorize('green', '\nWorkflow implementation complete!\n'));
70
+ }
52
71
  this.displaySuccess(result);
53
72
  }
54
73
  catch (error) {
@@ -1,8 +1,8 @@
1
1
  /* eslint-disable no-restricted-syntax, @typescript-eslint/no-explicit-any, init-declarations */
2
2
  import { describe, it, expect, beforeEach, vi } from 'vitest';
3
3
  import Generate from './generate.js';
4
- import { generateWorkflow } from '../../services/workflow_generator.js';
5
- import { InvalidNameError, WorkflowExistsError } from '../../types/errors.js';
4
+ import { generateWorkflow } from '#services/workflow_generator.js';
5
+ import { InvalidNameError, WorkflowExistsError } from '#types/errors.js';
6
6
  vi.mock('../../services/workflow_generator.js');
7
7
  describe('Generate Command', () => {
8
8
  let mockGenerateWorkflow;
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- import { type Workflow } from '../../api/generated/api.js';
2
+ import { type Workflow } from '#api/generated/api.js';
3
3
  interface WorkflowDisplay {
4
4
  name: string;
5
5
  description: string;
@@ -11,9 +11,9 @@ export default class WorkflowList extends Command {
11
11
  static description: string;
12
12
  static examples: string[];
13
13
  static flags: {
14
- format: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
15
- detailed: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
16
- filter: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
14
+ format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
15
+ detailed: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
16
+ filter: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
17
  };
18
18
  run(): Promise<void>;
19
19
  catch(error: Error): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
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';
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
6
  const OUTPUT_FORMAT = {
7
7
  LIST: 'list',
8
8
  TABLE: 'table',
@@ -3,10 +3,10 @@ export default class WorkflowOutput extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
- workflowId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
+ workflowId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
- format: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
10
  };
11
11
  run(): Promise<void>;
12
12
  catch(error: Error): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import { getWorkflowIdOutput } from '../../api/generated/api.js';
3
- import { OUTPUT_FORMAT } from '../../utils/constants.js';
4
- import { formatOutput } from '../../utils/output_formatter.js';
5
- import { handleApiError } from '../../utils/error_handler.js';
2
+ import { getWorkflowIdOutput } from '#api/generated/api.js';
3
+ import { OUTPUT_FORMAT } from '#utils/constants.js';
4
+ import { formatOutput } from '#utils/output_formatter.js';
5
+ import { handleApiError } from '#utils/error_handler.js';
6
6
  export default class WorkflowOutput extends Command {
7
7
  static description = 'Get workflow execution output';
8
8
  static examples = [
@@ -3,8 +3,8 @@ export default class WorkflowPlan extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
- 'force-agent-file-write': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
- description: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
+ 'force-agent-file-write': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
8
  };
9
9
  run(): Promise<void>;
10
10
  private planModificationLoop;
@@ -1,7 +1,8 @@
1
1
  import { Command, Flags, ux } from '@oclif/core';
2
2
  import { input } from '@inquirer/prompts';
3
- import { ensureOutputAIStructure, generatePlanName, updateAgentTemplates, writePlanFile } from '../../services/workflow_planner.js';
4
- import { invokePlanWorkflow, replyToClaude } from '../../services/claude_client.js';
3
+ import { generatePlanName, updateAgentTemplates, writePlanFile } from '#services/workflow_planner.js';
4
+ import { ensureOutputAIStructure } from '#services/coding_agents.js';
5
+ import { invokePlanWorkflow, PLAN_COMMAND_OPTIONS, replyToClaude } from '#services/claude_client.js';
5
6
  export default class WorkflowPlan extends Command {
6
7
  static description = 'Generate a workflow plan from a description';
7
8
  static examples = [
@@ -51,7 +52,7 @@ export default class WorkflowPlan extends Command {
51
52
  if (modifications === acceptKey) {
52
53
  return originalPlanContent;
53
54
  }
54
- const modifiedPlanContent = await replyToClaude(modifications);
55
+ const modifiedPlanContent = await replyToClaude(modifications, PLAN_COMMAND_OPTIONS);
55
56
  return this.planModificationLoop(modifiedPlanContent);
56
57
  }
57
58
  async planGenerationLoop(promptDescription, planName, projectRoot) {
@@ -1,10 +1,12 @@
1
1
  import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
2
2
  import WorkflowPlan from './plan.js';
3
- import { ensureOutputAIStructure, generatePlanName, writePlanFile, updateAgentTemplates } from '../../services/workflow_planner.js';
4
- import { invokePlanWorkflow, replyToClaude, ClaudeInvocationError } from '../../services/claude_client.js';
3
+ import { generatePlanName, writePlanFile, updateAgentTemplates } from '#services/workflow_planner.js';
4
+ import { ensureOutputAIStructure } from '#services/coding_agents.js';
5
+ import { invokePlanWorkflow, replyToClaude, ClaudeInvocationError } from '#services/claude_client.js';
5
6
  import { input } from '@inquirer/prompts';
6
- vi.mock('../../services/workflow_planner.js');
7
- vi.mock('../../services/claude_client.js');
7
+ vi.mock('#services/workflow_planner.js');
8
+ vi.mock('#services/coding_agents.js');
9
+ vi.mock('#services/claude_client.js');
8
10
  vi.mock('@inquirer/prompts');
9
11
  describe('WorkflowPlan Command', () => {
10
12
  const createCommand = () => {
@@ -3,12 +3,12 @@ export default class WorkflowRun extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
- workflowName: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
+ workflowName: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
- input: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
- 'task-queue': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
- format: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ input: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ 'task-queue': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
12
  };
13
13
  run(): Promise<void>;
14
14
  catch(error: Error): Promise<void>;
@@ -1,9 +1,9 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import { postWorkflowRun } from '../../api/generated/api.js';
3
- import { OUTPUT_FORMAT } from '../../utils/constants.js';
4
- import { parseInputFlag } from '../../utils/input_parser.js';
5
- import { formatOutput } from '../../utils/output_formatter.js';
6
- import { handleApiError } from '../../utils/error_handler.js';
2
+ import { postWorkflowRun } from '#api/generated/api.js';
3
+ import { OUTPUT_FORMAT } from '#utils/constants.js';
4
+ import { parseInputFlag } from '#utils/input_parser.js';
5
+ import { formatOutput } from '#utils/output_formatter.js';
6
+ import { handleApiError } from '#utils/error_handler.js';
7
7
  export default class WorkflowRun extends Command {
8
8
  static description = 'Execute a workflow synchronously and wait for completion';
9
9
  static examples = [
@@ -3,11 +3,11 @@ export default class WorkflowStart extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
- workflowName: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
+ workflowName: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
- input: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
- 'task-queue': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ input: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ 'task-queue': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
11
  };
12
12
  run(): Promise<void>;
13
13
  catch(error: Error): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import { postWorkflowStart } from '../../api/generated/api.js';
3
- import { parseInputFlag } from '../../utils/input_parser.js';
4
- import { handleApiError } from '../../utils/error_handler.js';
2
+ import { postWorkflowStart } from '#api/generated/api.js';
3
+ import { parseInputFlag } from '#utils/input_parser.js';
4
+ import { handleApiError } from '#utils/error_handler.js';
5
5
  export default class WorkflowStart extends Command {
6
6
  static description = 'Start a workflow asynchronously without waiting for completion';
7
7
  static examples = [
@@ -3,10 +3,10 @@ export default class WorkflowStatus extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
- workflowId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
+ workflowId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
- format: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
10
  };
11
11
  run(): Promise<void>;
12
12
  catch(error: Error): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import { getWorkflowIdStatus } from '../../api/generated/api.js';
3
- import { OUTPUT_FORMAT } from '../../utils/constants.js';
4
- import { formatOutput } from '../../utils/output_formatter.js';
5
- import { handleApiError } from '../../utils/error_handler.js';
2
+ import { getWorkflowIdStatus } from '#api/generated/api.js';
3
+ import { OUTPUT_FORMAT } from '#utils/constants.js';
4
+ import { formatOutput } from '#utils/output_formatter.js';
5
+ import { handleApiError } from '#utils/error_handler.js';
6
6
  export default class WorkflowStatus extends Command {
7
7
  static description = 'Get workflow execution status';
8
8
  static examples = [
@@ -3,7 +3,7 @@ export default class WorkflowStop extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
- workflowId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
+ workflowId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  run(): Promise<void>;
9
9
  catch(error: Error): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { Args, Command } from '@oclif/core';
2
- import { patchWorkflowIdStop } from '../../api/generated/api.js';
3
- import { handleApiError } from '../../utils/error_handler.js';
2
+ import { patchWorkflowIdStop } from '#api/generated/api.js';
3
+ import { handleApiError } from '#utils/error_handler.js';
4
4
  export default class WorkflowStop extends Command {
5
5
  static description = 'Stop a workflow execution';
6
6
  static examples = [
@@ -1,8 +1,14 @@
1
+ /**
2
+ * Claude Agent SDK client for workflow planning
3
+ */
4
+ import { Options } from '@anthropic-ai/claude-agent-sdk';
5
+ export declare const PLAN_COMMAND_OPTIONS: Options;
6
+ export declare const BUILD_COMMAND_OPTIONS: Options;
1
7
  export declare class ClaudeInvocationError extends Error {
2
8
  cause?: Error | undefined;
3
9
  constructor(message: string, cause?: Error | undefined);
4
10
  }
5
- export declare function replyToClaude(message: string): Promise<string>;
11
+ export declare function replyToClaude(message: string, options?: Options): Promise<string>;
6
12
  /**
7
13
  * Invoke claude-code with /plan_workflow slash command
8
14
  * The SDK loads custom commands from .claude/commands/ when settingSources includes 'project'.
@@ -11,3 +17,14 @@ export declare function replyToClaude(message: string): Promise<string>;
11
17
  * @returns Plan output from claude-code
12
18
  */
13
19
  export declare function invokePlanWorkflow(description: string): Promise<string>;
20
+ /**
21
+ * Invoke claude-code with /build_workflow slash command
22
+ * The SDK loads custom commands from .claude/commands/ when settingSources includes 'project'.
23
+ * ensureOutputAIStructure() scaffolds the command files to that location.
24
+ * @param planFilePath - Absolute path to the plan file
25
+ * @param workflowDir - Absolute path to the workflow directory
26
+ * @param workflowName - Name of the workflow
27
+ * @param additionalInstructions - Optional additional instructions
28
+ * @returns Implementation output from claude-code
29
+ */
30
+ export declare function invokeBuildWorkflow(planFilePath: string, workflowDir: string, workflowName: string, additionalInstructions?: string): Promise<string>;
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { query } from '@anthropic-ai/claude-agent-sdk';
5
5
  import { ux } from '@oclif/core';
6
- import ora from 'ora';
6
+ import * as cliProgress from 'cli-progress';
7
7
  const ADDITIONAL_INSTRUCTIONS = `
8
8
  ! IMPORTANT !
9
9
  1. Use TodoWrite to track your progress through plan creation.
@@ -22,16 +22,26 @@ date: <plan-date>
22
22
 
23
23
  4. After you mark all todos as complete, you must respond with the final version of the plan.
24
24
  `;
25
+ const ADDITIONAL_INSTRUCTIONS_BUILD = `
26
+ ! IMPORTANT !
27
+ 1. Use TodoWrite to track your progress through workflow implementation.
28
+
29
+ 2. Follow the implementation plan exactly as specified in the plan file.
30
+
31
+ 3. Implement all workflow files following Output SDK patterns and best practices.
32
+
33
+ 4. After you mark all todos as complete, provide a summary of what was implemented.
34
+ `;
25
35
  const PLAN_COMMAND = 'plan_workflow';
36
+ const BUILD_COMMAND = 'build_workflow';
26
37
  const GLOBAL_CLAUDE_OPTIONS = {
27
- settingSources: ['user', 'project', 'local'],
28
- allowedTools: [
29
- 'Read',
30
- 'Grep',
31
- 'WebSearch',
32
- 'WebFetch',
33
- 'TodoWrite'
34
- ]
38
+ settingSources: ['user', 'project', 'local']
39
+ };
40
+ export const PLAN_COMMAND_OPTIONS = {
41
+ allowedTools: ['Read', 'Grep', 'WebSearch', 'WebFetch', 'TodoWrite']
42
+ };
43
+ export const BUILD_COMMAND_OPTIONS = {
44
+ permissionMode: 'bypassPermissions'
35
45
  };
36
46
  export class ClaudeInvocationError extends Error {
37
47
  cause;
@@ -47,16 +57,23 @@ function validateEnvironment() {
47
57
  }
48
58
  }
49
59
  function validateSystem(systemMessage) {
50
- const requiredCommands = [PLAN_COMMAND];
60
+ const requiredCommands = [PLAN_COMMAND, BUILD_COMMAND];
51
61
  const availableCommands = systemMessage.slash_commands;
52
62
  const missingCommands = requiredCommands.filter(command => !availableCommands.includes(command));
53
- for (const command of missingCommands) {
54
- ux.warn(`Missing required claude-code slash command: /${command}`);
55
- }
56
- if (missingCommands.length > 0) {
57
- ux.warn('Your claude-code agent is missing key configurations, it may not behave as expected.');
58
- ux.warn('Please run "output-cli agents init" to fix this.');
63
+ return {
64
+ missingCommands,
65
+ hasIssues: missingCommands.length > 0
66
+ };
67
+ }
68
+ function displaySystemValidationWarnings(validation) {
69
+ if (!validation.hasIssues) {
70
+ return;
59
71
  }
72
+ validation.missingCommands.forEach(command => {
73
+ ux.warn(`Missing required claude-code slash command: /${command}`);
74
+ });
75
+ ux.warn('Your claude-code agent is missing key configurations, it may not behave as expected.');
76
+ ux.warn('Please run "output-cli agents init" to fix this.');
60
77
  }
61
78
  function applyDefaultOptions(options) {
62
79
  return {
@@ -71,46 +88,89 @@ function getTodoWriteMessage(message) {
71
88
  const todoWriteMessage = message.message.content.find((c) => c?.type === 'tool_use' && c.name === 'TodoWrite');
72
89
  return todoWriteMessage ?? null;
73
90
  }
74
- function getInProgressTodo(message) {
91
+ function applyInstructions(initialMessage, instructionsType = 'plan') {
92
+ const instructions = instructionsType === 'build' ?
93
+ ADDITIONAL_INSTRUCTIONS_BUILD :
94
+ ADDITIONAL_INSTRUCTIONS;
95
+ return `${initialMessage}\n\n${instructions}`;
96
+ }
97
+ function createProgressBar() {
98
+ return new cliProgress.SingleBar({
99
+ format: '{bar} | {message} ({percentage}%)',
100
+ barCompleteChar: '█',
101
+ barIncompleteChar: '░',
102
+ hideCursor: true,
103
+ barsize: 40,
104
+ fps: 10,
105
+ stopOnComplete: false
106
+ });
107
+ }
108
+ function calculateProgress(completedCount, totalCount) {
109
+ if (totalCount === 0) {
110
+ return 0;
111
+ }
112
+ const percentage = ((completedCount + 1) / (totalCount + 1)) * 100;
113
+ return Math.round(percentage * 10) / 10;
114
+ }
115
+ function getProgressUpdate(message) {
75
116
  const todoWriteMessage = getTodoWriteMessage(message);
76
117
  if (!todoWriteMessage) {
77
118
  return null;
78
119
  }
79
- const inProgressTodo = todoWriteMessage.input.todos.find(t => t.status === 'in_progress');
80
- return inProgressTodo?.content ?? null;
120
+ const allTodos = todoWriteMessage.input.todos;
121
+ const inProgressTodo = allTodos.find(t => t.status === 'in_progress');
122
+ if (!inProgressTodo?.content) {
123
+ return null;
124
+ }
125
+ const completedTodos = allTodos.filter(t => t.status === 'completed');
126
+ return {
127
+ message: `${inProgressTodo.content}...`,
128
+ completed: completedTodos.length,
129
+ total: allTodos.length
130
+ };
81
131
  }
82
- function applyInstructions(initialMessage) {
83
- return `${initialMessage}\n\n${ADDITIONAL_INSTRUCTIONS}`;
132
+ function debugMessage(message) {
133
+ if (process.env.DEBUG !== 'true') {
134
+ return;
135
+ }
136
+ ux.stdout(ux.colorize('teal', `[Message]: ${message.type}`));
137
+ ux.stdout(ux.colorize('teal', `[JSON]: ${JSON.stringify(message, null, 2)}`));
84
138
  }
85
139
  async function singleQuery(prompt, options = {}) {
86
140
  validateEnvironment();
87
- const spinner = ora('Thinking...').start();
141
+ const progressBar = createProgressBar();
142
+ progressBar.start(100, 0, { message: 'Thinking...' });
88
143
  try {
89
144
  for await (const message of query({
90
145
  prompt,
91
146
  options: applyDefaultOptions(options)
92
147
  })) {
148
+ debugMessage(message);
93
149
  if (message.type === 'system' && message.subtype === 'init') {
94
- validateSystem(message);
95
- spinner.text = 'Diving in...';
150
+ const validation = validateSystem(message);
151
+ displaySystemValidationWarnings(validation);
152
+ progressBar.update(1, { message: 'Diving in...' });
96
153
  }
97
- const inProgressTodo = getInProgressTodo(message);
98
- if (inProgressTodo) {
99
- spinner.text = `${inProgressTodo}...`;
154
+ const progressUpdate = getProgressUpdate(message);
155
+ if (progressUpdate) {
156
+ const percentage = calculateProgress(progressUpdate.completed, progressUpdate.total);
157
+ progressBar.update(percentage, { message: progressUpdate.message });
100
158
  }
101
159
  if (message.type === 'result' && message.subtype === 'success') {
102
- spinner.stop();
160
+ progressBar.update(100, { message: 'Complete!' });
161
+ progressBar.stop();
103
162
  return message.result;
104
163
  }
105
164
  }
106
165
  throw new Error('No output received from claude-code');
107
166
  }
108
167
  catch (error) {
168
+ progressBar.stop();
109
169
  throw new ClaudeInvocationError(`Failed to invoke claude-code: ${error.message}`, error);
110
170
  }
111
171
  }
112
- export async function replyToClaude(message) {
113
- return singleQuery(applyInstructions(message), { continue: true });
172
+ export async function replyToClaude(message, options = {}) {
173
+ return singleQuery(applyInstructions(message), { continue: true, ...options });
114
174
  }
115
175
  /**
116
176
  * Invoke claude-code with /plan_workflow slash command
@@ -120,5 +180,22 @@ export async function replyToClaude(message) {
120
180
  * @returns Plan output from claude-code
121
181
  */
122
182
  export async function invokePlanWorkflow(description) {
123
- return singleQuery(applyInstructions(`/${PLAN_COMMAND} ${description}`));
183
+ return singleQuery(applyInstructions(`/${PLAN_COMMAND} ${description}`), PLAN_COMMAND_OPTIONS);
184
+ }
185
+ /**
186
+ * Invoke claude-code with /build_workflow slash command
187
+ * The SDK loads custom commands from .claude/commands/ when settingSources includes 'project'.
188
+ * ensureOutputAIStructure() scaffolds the command files to that location.
189
+ * @param planFilePath - Absolute path to the plan file
190
+ * @param workflowDir - Absolute path to the workflow directory
191
+ * @param workflowName - Name of the workflow
192
+ * @param additionalInstructions - Optional additional instructions
193
+ * @returns Implementation output from claude-code
194
+ */
195
+ export async function invokeBuildWorkflow(planFilePath, workflowDir, workflowName, additionalInstructions) {
196
+ const commandArgs = `${planFilePath} ${workflowName} ${workflowDir}`;
197
+ const fullCommand = additionalInstructions ?
198
+ `/${BUILD_COMMAND} ${commandArgs} ${additionalInstructions}` :
199
+ `/${BUILD_COMMAND} ${commandArgs}`;
200
+ return singleQuery(applyInstructions(fullCommand, 'build'), BUILD_COMMAND_OPTIONS);
124
201
  }
@@ -41,3 +41,10 @@ export declare function prepareTemplateVariables(): Record<string, string>;
41
41
  * Main entry point for agent initialization logic
42
42
  */
43
43
  export declare function initializeAgentConfig(options: InitOptions): Promise<void>;
44
+ /**
45
+ * Ensure .outputai directory structure exists by invoking agents init if needed
46
+ * Displays warnings for missing files and prompts for reinitialization
47
+ * @param projectRoot - Root directory of the project
48
+ * @throws Error if user declines to initialize or if initialization fails
49
+ */
50
+ export declare function ensureOutputAIStructure(projectRoot: string): Promise<void>;