@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.
- package/README.md +2 -3
- package/dist/api/http_client.js +1 -1
- package/dist/commands/agents/init.d.ts +2 -2
- package/dist/commands/agents/init.js +2 -2
- package/dist/commands/agents/init.spec.js +2 -2
- package/dist/commands/workflow/generate.d.ts +6 -5
- package/dist/commands/workflow/generate.js +25 -6
- package/dist/commands/workflow/generate.spec.js +2 -2
- package/dist/commands/workflow/list.d.ts +4 -4
- package/dist/commands/workflow/list.js +3 -3
- package/dist/commands/workflow/output.d.ts +2 -2
- package/dist/commands/workflow/output.js +4 -4
- package/dist/commands/workflow/plan.d.ts +2 -2
- package/dist/commands/workflow/plan.js +4 -3
- package/dist/commands/workflow/plan.spec.js +6 -4
- package/dist/commands/workflow/run.d.ts +4 -4
- package/dist/commands/workflow/run.js +5 -5
- package/dist/commands/workflow/start.d.ts +3 -3
- package/dist/commands/workflow/start.js +3 -3
- package/dist/commands/workflow/status.d.ts +2 -2
- package/dist/commands/workflow/status.js +4 -4
- package/dist/commands/workflow/stop.d.ts +1 -1
- package/dist/commands/workflow/stop.js +2 -2
- package/dist/services/claude_client.d.ts +18 -1
- package/dist/services/claude_client.js +108 -31
- package/dist/services/coding_agents.d.ts +7 -0
- package/dist/services/coding_agents.js +67 -10
- package/dist/services/coding_agents.spec.js +155 -14
- package/dist/services/template_processor.d.ts +1 -1
- package/dist/services/template_processor.js +1 -1
- package/dist/services/workflow_builder.d.ts +16 -0
- package/dist/services/workflow_builder.js +85 -0
- package/dist/services/workflow_builder.spec.d.ts +1 -0
- package/dist/services/workflow_builder.spec.js +165 -0
- package/dist/services/workflow_generator.d.ts +1 -1
- package/dist/services/workflow_generator.js +4 -4
- package/dist/services/workflow_planner.d.ts +0 -5
- package/dist/services/workflow_planner.js +2 -39
- package/dist/services/workflow_planner.spec.js +2 -77
- package/dist/templates/agent_instructions/commands/build_workflow.md.template +246 -0
- package/dist/templates/workflow/steps.ts.template +25 -54
- package/dist/templates/workflow/workflow.ts.template +23 -49
- package/dist/types/domain.d.ts +20 -0
- package/dist/types/domain.js +4 -0
- package/dist/utils/error_handler.js +1 -1
- package/dist/utils/paths.d.ts +1 -1
- package/dist/utils/paths.js +1 -1
- package/dist/utils/validation.js +1 -1
- 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: `
|
|
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
|
-
├──
|
|
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
|
```
|
package/dist/api/http_client.js
CHANGED
|
@@ -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
|
|
8
|
-
force: import("@oclif/core/lib/interfaces
|
|
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 '
|
|
3
|
-
import { initializeAgentConfig, AGENT_CONFIGS } from '
|
|
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 '
|
|
7
|
-
import { processTemplate } from '
|
|
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
|
|
7
|
-
description: import("@oclif/core/lib/interfaces
|
|
8
|
-
'output-dir': import("@oclif/core/lib/interfaces
|
|
9
|
-
force: import("@oclif/core/lib/interfaces
|
|
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
|
|
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 '
|
|
3
|
-
import {
|
|
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
|
-
|
|
42
|
-
|
|
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 '
|
|
5
|
-
import { InvalidNameError, WorkflowExistsError } from '
|
|
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 '
|
|
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
|
|
15
|
-
detailed: import("@oclif/core/lib/interfaces
|
|
16
|
-
filter: import("@oclif/core/lib/interfaces
|
|
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 '
|
|
4
|
-
import { parseWorkflowDefinition, formatParameters } from '
|
|
5
|
-
import { handleApiError } from '
|
|
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
|
|
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
|
|
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 '
|
|
3
|
-
import { OUTPUT_FORMAT } from '
|
|
4
|
-
import { formatOutput } from '
|
|
5
|
-
import { handleApiError } from '
|
|
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
|
|
7
|
-
description: import("@oclif/core/lib/interfaces
|
|
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 {
|
|
4
|
-
import {
|
|
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 {
|
|
4
|
-
import {
|
|
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('
|
|
7
|
-
vi.mock('
|
|
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
|
|
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
|
|
10
|
-
'task-queue': import("@oclif/core/lib/interfaces
|
|
11
|
-
format: import("@oclif/core/lib/interfaces
|
|
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 '
|
|
3
|
-
import { OUTPUT_FORMAT } from '
|
|
4
|
-
import { parseInputFlag } from '
|
|
5
|
-
import { formatOutput } from '
|
|
6
|
-
import { handleApiError } from '
|
|
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
|
|
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
|
|
10
|
-
'task-queue': import("@oclif/core/lib/interfaces
|
|
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 '
|
|
3
|
-
import { parseInputFlag } from '
|
|
4
|
-
import { handleApiError } from '
|
|
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
|
|
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
|
|
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 '
|
|
3
|
-
import { OUTPUT_FORMAT } from '
|
|
4
|
-
import { formatOutput } from '
|
|
5
|
-
import { handleApiError } from '
|
|
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
|
|
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 '
|
|
3
|
-
import { handleApiError } from '
|
|
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
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
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
|
|
80
|
-
|
|
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
|
|
83
|
-
|
|
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
|
|
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
|
-
|
|
150
|
+
const validation = validateSystem(message);
|
|
151
|
+
displaySystemValidationWarnings(validation);
|
|
152
|
+
progressBar.update(1, { message: 'Diving in...' });
|
|
96
153
|
}
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
|
|
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
|
-
|
|
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>;
|