@tmddev/tmd 0.1.0 → 0.3.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.
@@ -4,129 +4,113 @@ import { getSkillsDir, getSkillDir } from './paths.js';
4
4
  import { generateTaskId } from './task-id.js';
5
5
  import yaml from 'js-yaml';
6
6
  import chalk from 'chalk';
7
- export function createSkill(description, taskId) {
7
+ import { executeStep as executeStepWithExecutor } from './step-executor.js';
8
+ /**
9
+ * Shared logic to write a skill directory. Used by createSkill (tmd plan --skill).
10
+ */
11
+ function writeSkillDir(skillName, description, tags, createdFrom) {
8
12
  const skillsDir = getSkillsDir();
9
13
  ensureDir(skillsDir);
10
- // Generate skill name from description
11
- const skillName = generateTaskId(description).replace(/^\d+-/, '');
12
14
  const skillDir = getSkillDir(skillName);
13
- if (existsSync(skillDir)) {
14
- console.log(chalk.yellow(` Skill already exists: ${skillName}`));
15
- return skillName;
16
- }
17
15
  mkdirSync(skillDir, { recursive: true });
18
- // Create skill.md with user story template
19
- const skillContent = `# Skill: ${description}
20
-
21
- ## User Story
22
- As a [role], I want to [action], so as to [benefit]
16
+ const skillContent = `---
17
+ name: ${skillName}
18
+ description: ${description}
19
+ ---
23
20
 
24
- ## Description
25
- ${description}
21
+ # ${description}
26
22
 
27
- ## Prerequisites
28
- <!-- List prerequisites -->
23
+ ## Usage
29
24
 
30
- ## Skill Steps
31
- <!-- Define executable steps -->
32
- 1.
33
- 2.
34
- 3.
25
+ Define your skill steps in \`steps.yaml\` and configure parameters in \`config.yaml\`.
35
26
 
36
- ## Expected Outcomes
37
- <!-- Define expected results -->
27
+ ## Steps
38
28
 
39
- ## Validation Criteria
40
- <!-- Define how to validate success -->
29
+ See \`steps.yaml\` for executable steps configuration.
41
30
  `;
42
- writeFileSync(join(skillDir, 'skill.md'), skillContent);
43
- // Create steps.yaml
31
+ writeFileSync(join(skillDir, 'SKILL.md'), skillContent);
44
32
  const stepsContent = {
45
- steps: [
46
- {
47
- type: 'example',
48
- description: 'Example step',
49
- // Add step configuration
50
- }
51
- ]
33
+ steps: [{ type: 'example', description: 'Example step' }]
52
34
  };
53
35
  writeFileSync(join(skillDir, 'steps.yaml'), yaml.dump(stepsContent));
54
- // Create config.yaml
55
- const configContent = {
56
- // Configuration for skill execution
57
- };
58
- writeFileSync(join(skillDir, 'config.yaml'), yaml.dump(configContent));
59
- // Create metadata.yaml
36
+ writeFileSync(join(skillDir, 'config.yaml'), yaml.dump({}));
60
37
  const metadataContent = {
61
38
  name: skillName,
62
39
  version: '1.0.0',
63
- description: description,
64
- tags: [],
65
- createdFrom: taskId,
66
- interface: {
67
- input: [],
68
- output: {}
69
- }
40
+ description,
41
+ tags,
42
+ interface: { input: [], output: {} }
70
43
  };
44
+ if (createdFrom != null && createdFrom !== '') {
45
+ metadataContent['createdFrom'] = createdFrom;
46
+ }
71
47
  writeFileSync(join(skillDir, 'metadata.yaml'), yaml.dump(metadataContent));
48
+ }
49
+ /**
50
+ * Create a skill from a plan (tmd plan --skill). Generates name from description.
51
+ * Options.tags is applied when provided; options.taskId is used as createdFrom when provided.
52
+ */
53
+ export function createSkill(description, taskId, options) {
54
+ const skillsDir = getSkillsDir();
55
+ ensureDir(skillsDir);
56
+ const skillName = generateTaskId(description).replace(/^\d+-/, '');
57
+ const skillDir = getSkillDir(skillName);
58
+ if (existsSync(skillDir)) {
59
+ console.log(chalk.yellow(` Skill already exists: ${skillName}`));
60
+ return skillName;
61
+ }
62
+ const tags = options?.tags ?? [];
63
+ writeSkillDir(skillName, description, tags, taskId);
72
64
  return skillName;
73
65
  }
74
- function executeStep(step, stepIndex) {
75
- return new Promise((resolve) => {
76
- const startTime = Date.now();
77
- try {
78
- // Basic step execution based on type
79
- switch (step.type) {
80
- case 'database':
81
- console.log(chalk.yellow(` Database operation: ${step.operation ?? 'query'}`));
82
- // TODO: Implement database operations
83
- break;
84
- case 'file':
85
- console.log(chalk.yellow(` File operation: ${step.operation ?? 'read'}`));
86
- // TODO: Implement file operations
87
- break;
88
- case 'script':
89
- console.log(chalk.yellow(` Script execution: ${step.script ?? 'unknown'}`));
90
- // TODO: Implement script execution
91
- break;
92
- case 'api':
93
- console.log(chalk.yellow(` API call: ${step.endpoint ?? 'unknown'}`));
94
- // TODO: Implement API calls
95
- break;
96
- case 'image':
97
- console.log(chalk.yellow(` Image processing: ${step.operation ?? 'process'}`));
98
- // TODO: Implement image processing
99
- break;
100
- default:
101
- console.log(chalk.yellow(` Unknown step type: ${step.type ?? 'undefined'}`));
66
+ async function executeStep(step, stepIndex, taskId, workingDir) {
67
+ const startTime = Date.now();
68
+ try {
69
+ const context = {
70
+ taskId,
71
+ workingDir,
72
+ dryRun: false
73
+ };
74
+ const result = await executeStepWithExecutor(step, context);
75
+ if (result.success) {
76
+ console.log(chalk.green(` ✓ Step ${String(stepIndex + 1)}: ${step.description ?? step.type}`));
77
+ if (result.output) {
78
+ console.log(chalk.gray(` ${result.output}`));
102
79
  }
103
- const duration = Date.now() - startTime;
104
- resolve({
105
- stepIndex,
106
- success: true,
107
- duration
108
- });
109
80
  }
110
- catch (error) {
111
- const duration = Date.now() - startTime;
112
- const errorMessage = error instanceof Error ? error.message : String(error);
113
- resolve({
114
- stepIndex,
115
- success: false,
116
- error: errorMessage,
117
- duration
118
- });
81
+ else {
82
+ console.log(chalk.red(` ✗ Step ${String(stepIndex + 1)}: ${step.description ?? step.type}`));
83
+ if (result.error) {
84
+ console.log(chalk.red(` ${result.error}`));
85
+ }
119
86
  }
120
- });
87
+ return {
88
+ stepIndex,
89
+ success: result.success,
90
+ ...(result.error != null ? { error: result.error } : {}),
91
+ duration: result.duration
92
+ };
93
+ }
94
+ catch (error) {
95
+ const duration = Date.now() - startTime;
96
+ const errorMessage = error instanceof Error ? error.message : String(error);
97
+ console.log(chalk.red(` ✗ Step ${String(stepIndex + 1)}: ${errorMessage}`));
98
+ return {
99
+ stepIndex,
100
+ success: false,
101
+ error: errorMessage,
102
+ duration
103
+ };
104
+ }
121
105
  }
122
- async function executeStepsConcurrently(steps, maxConcurrency = Infinity) {
106
+ async function executeStepsConcurrently(steps, maxConcurrency, taskId, workingDir) {
123
107
  const results = [];
124
108
  const executing = [];
125
109
  for (let i = 0; i < steps.length; i++) {
126
110
  const step = steps[i];
127
111
  if (!step)
128
112
  continue;
129
- const stepDeps = step.depends ?? [];
113
+ const stepDeps = Array.isArray(step['depends']) ? step['depends'] : [];
130
114
  // Check if dependencies are satisfied (simplified - assumes sequential if dependencies exist)
131
115
  const hasDependencies = Array.isArray(stepDeps) && stepDeps.length > 0;
132
116
  if (hasDependencies) {
@@ -136,60 +120,60 @@ async function executeStepsConcurrently(steps, maxConcurrency = Infinity) {
136
120
  await Promise.all(executing);
137
121
  executing.length = 0;
138
122
  }
139
- const promise = executeStep(step, i);
123
+ const promise = executeStep(step, i, taskId, workingDir);
140
124
  executing.push(promise);
141
125
  if (executing.length >= maxConcurrency) {
142
126
  const completed = await Promise.race(executing);
143
127
  results.push(completed);
144
- // Remove one promise from executing (the one that completed)
145
- // We can't reliably identify which one, so remove the first
146
- // The promise is already resolved by Promise.race, so we can safely remove it
147
- const removedPromise = executing.shift();
148
- // Explicitly mark as handled to satisfy ESLint
149
- void removedPromise;
128
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises -- pool mutation; remaining promises are handled by Promise.allSettled below
129
+ executing.splice(executing.indexOf(promise), 1);
150
130
  }
151
131
  }
152
132
  // Wait for remaining steps
153
- if (executing.length > 0) {
154
- const remainingResults = await Promise.allSettled(executing);
155
- for (const result of remainingResults) {
156
- if (result.status === 'fulfilled') {
157
- results.push(result.value);
158
- }
159
- else {
160
- // Handle rejected promise - reason can be any type
161
- const reason = result.reason;
162
- let errorMessage;
163
- if (reason instanceof Error) {
164
- errorMessage = reason.message;
165
- }
166
- else if (typeof reason === 'string') {
167
- errorMessage = reason;
168
- }
169
- else if (reason === null || reason === undefined) {
170
- errorMessage = 'Unknown error';
171
- }
172
- else {
173
- // For other types, use JSON.stringify if possible, otherwise fallback
174
- try {
175
- errorMessage = JSON.stringify(reason);
176
- }
177
- catch {
178
- errorMessage = 'Unknown error';
179
- }
180
- }
181
- results.push({
182
- stepIndex: -1,
183
- success: false,
184
- error: errorMessage,
185
- duration: 0
186
- });
187
- }
133
+ const remainingResults = await Promise.allSettled(executing);
134
+ for (const result of remainingResults) {
135
+ if (result.status === 'fulfilled') {
136
+ results.push(result.value);
137
+ }
138
+ else {
139
+ results.push({
140
+ stepIndex: -1,
141
+ success: false,
142
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
143
+ duration: 0
144
+ });
188
145
  }
189
146
  }
190
147
  return results.sort((a, b) => a.stepIndex - b.stepIndex);
191
148
  }
192
- export async function executeSkill(skillName, _taskId, parallel = false, maxConcurrency = Infinity) {
149
+ function replaceVariables(obj, params) {
150
+ if (typeof obj === 'string') {
151
+ // Replace ${variable} with param value
152
+ const result = obj.replace(/\$\{(\w+)\}/g, (match, key) => {
153
+ return params[key] ?? match;
154
+ });
155
+ // Try to convert to number if the original was a number-like string
156
+ if (/^\d+$/.test(result)) {
157
+ const num = Number(result);
158
+ if (!isNaN(num)) {
159
+ return num;
160
+ }
161
+ }
162
+ return result;
163
+ }
164
+ else if (Array.isArray(obj)) {
165
+ return obj.map(item => replaceVariables(item, params));
166
+ }
167
+ else if (obj && typeof obj === 'object') {
168
+ const result = {};
169
+ for (const [key, value] of Object.entries(obj)) {
170
+ result[key] = replaceVariables(value, params);
171
+ }
172
+ return result;
173
+ }
174
+ return obj;
175
+ }
176
+ export async function executeSkill(skillName, taskId, parallel = false, maxConcurrency = Infinity, params = {}) {
193
177
  const skillDir = getSkillDir(skillName);
194
178
  if (!existsSync(skillDir)) {
195
179
  console.error(chalk.red(`✗ Skill not found: ${skillName}`));
@@ -201,17 +185,39 @@ export async function executeSkill(skillName, _taskId, parallel = false, maxConc
201
185
  return;
202
186
  }
203
187
  try {
188
+ // Load config.yaml for default values
189
+ const configPath = join(skillDir, 'config.yaml');
190
+ let configDefaults = {};
191
+ if (existsSync(configPath)) {
192
+ try {
193
+ const config = yaml.load(readFileSync(configPath, 'utf-8'));
194
+ configDefaults = config.defaults ?? {};
195
+ }
196
+ catch {
197
+ // Ignore config parsing errors
198
+ }
199
+ }
200
+ // Merge config defaults with provided params (params take precedence)
201
+ const mergedParams = { ...configDefaults, ...params };
202
+ // Convert all values to strings for variable replacement
203
+ const stringParams = {};
204
+ for (const [key, value] of Object.entries(mergedParams)) {
205
+ stringParams[key] = typeof value === 'string' ? value : String(value);
206
+ }
204
207
  const steps = yaml.load(readFileSync(stepsPath, 'utf-8'));
205
208
  console.log(chalk.blue(` Executing skill: ${skillName}`));
206
- console.log(chalk.gray(` Steps: ${String(steps.steps?.length ?? 0)}`));
209
+ console.log(chalk.gray(` Steps: ${String((steps.steps != null && Array.isArray(steps.steps) ? steps.steps : []).length)}`));
207
210
  console.log(chalk.gray(` Mode: ${parallel ? 'concurrent' : 'sequential'}`));
208
- if (steps.steps && Array.isArray(steps.steps)) {
211
+ const workingDir = skillDir;
212
+ // Replace variables in steps with merged parameters
213
+ const processedSteps = steps.steps != null ? replaceVariables(steps.steps, stringParams) : steps.steps;
214
+ if (processedSteps && Array.isArray(processedSteps)) {
209
215
  if (parallel) {
210
216
  // Execute steps concurrently
211
- const results = await executeStepsConcurrently(steps.steps, maxConcurrency);
217
+ const results = await executeStepsConcurrently(processedSteps, maxConcurrency, taskId, workingDir);
212
218
  const successCount = results.filter(r => r.success).length;
213
219
  const failCount = results.length - successCount;
214
- console.log(chalk.blue(` Execution Summary: ${String(successCount)} successful, ${String(failCount)} failed`));
220
+ console.log(chalk.blue(` Execution Summary: ${successCount.toString()} successful, ${failCount.toString()} failed`));
215
221
  if (failCount > 0) {
216
222
  const failedSteps = results.filter(r => !r.success);
217
223
  throw new Error(`Skill execution failed: ${String(failedSteps.length)} step(s) failed`);
@@ -219,15 +225,12 @@ export async function executeSkill(skillName, _taskId, parallel = false, maxConc
219
225
  }
220
226
  else {
221
227
  // Execute steps sequentially (original behavior)
222
- for (let i = 0; i < steps.steps.length; i++) {
223
- const step = steps.steps[i];
224
- if (!step)
225
- continue;
226
- console.log(chalk.gray(` Step ${String(i + 1)}/${String(steps.steps.length)}: ${step.type ?? 'unknown'}`));
227
- const result = await executeStep(step, i);
228
+ for (let i = 0; i < processedSteps.length; i++) {
229
+ const step = processedSteps[i];
230
+ console.log(chalk.gray(` Step ${String(i + 1)}/${String(processedSteps.length)}: ${step.type}`));
231
+ const result = await executeStep(step, i, taskId, workingDir);
228
232
  if (!result.success) {
229
- const errorMsg = result.error ?? 'Unknown error';
230
- throw new Error(`Step ${String(i + 1)} failed: ${errorMsg}`);
233
+ throw new Error(`Step ${String(i + 1)} failed: ${result.error ?? ''}`);
231
234
  }
232
235
  }
233
236
  }
@@ -235,8 +238,7 @@ export async function executeSkill(skillName, _taskId, parallel = false, maxConc
235
238
  console.log(chalk.green(` ✓ Skill execution completed: ${skillName}`));
236
239
  }
237
240
  catch (e) {
238
- const errorMessage = e instanceof Error ? e.message : String(e);
239
- console.error(chalk.red(`✗ Error executing skill: ${errorMessage}`));
241
+ console.error(chalk.red(`✗ Error executing skill: ${String(e)}`));
240
242
  throw e;
241
243
  }
242
244
  }
@@ -7,6 +7,6 @@ interface SkillsshResult {
7
7
  /**
8
8
  * Fetches and converts a skill from skills.sh to TMD format
9
9
  */
10
- export declare function addSkillFromSkillssh(ownerRepo: string): Promise<SkillsshResult>;
10
+ export declare function addSkillFromSkillssh(ownerRepo: string, skillName?: string): Promise<SkillsshResult>;
11
11
  export {};
12
12
  //# sourceMappingURL=skillssh.d.ts.map
@@ -5,7 +5,7 @@ import yaml from 'js-yaml';
5
5
  /**
6
6
  * Fetches and converts a skill from skills.sh to TMD format
7
7
  */
8
- export async function addSkillFromSkillssh(ownerRepo) {
8
+ export async function addSkillFromSkillssh(ownerRepo, skillName) {
9
9
  // Validate owner/repo format
10
10
  const parts = ownerRepo.split('/');
11
11
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
@@ -15,21 +15,22 @@ export async function addSkillFromSkillssh(ownerRepo) {
15
15
  };
16
16
  }
17
17
  const [owner, repo] = parts;
18
- const skillName = repo;
18
+ // Use provided skill name or derive from repo
19
+ const finalSkillName = skillName ?? repo;
19
20
  // Check if skill already exists
20
- const skillDir = getSkillDir(skillName);
21
+ const skillDir = getSkillDir(finalSkillName);
21
22
  if (existsSync(skillDir)) {
22
23
  return {
23
24
  success: false,
24
- error: `Skill already exists: ${skillName}`
25
+ error: `Skill already exists: ${finalSkillName}`
25
26
  };
26
27
  }
27
28
  // Fetch skill content from GitHub
28
- const skillContent = await fetchSkillContent(owner, repo);
29
+ const skillContent = await fetchSkillContent(owner, repo, skillName);
29
30
  if (!skillContent.success) {
30
31
  return {
31
32
  success: false,
32
- error: skillContent.error ?? 'Unknown error'
33
+ error: skillContent.error ?? 'Failed to fetch skill'
33
34
  };
34
35
  }
35
36
  // Create skill directory
@@ -39,21 +40,28 @@ export async function addSkillFromSkillssh(ownerRepo) {
39
40
  }
40
41
  mkdirSync(skillDir, { recursive: true });
41
42
  // Convert and save skill files
42
- saveSkillFiles(skillDir, skillName, ownerRepo, skillContent.content ?? '');
43
+ saveSkillFiles(skillDir, finalSkillName, ownerRepo, skillContent.content ?? '', skillName);
43
44
  return {
44
45
  success: true,
45
- skillName,
46
+ skillName: finalSkillName,
46
47
  skillDir
47
48
  };
48
49
  }
49
- async function fetchSkillContent(owner, repo) {
50
- // Try common skill file locations
51
- const possiblePaths = [
52
- `https://raw.githubusercontent.com/${owner}/${repo}/main/README.md`,
53
- `https://raw.githubusercontent.com/${owner}/${repo}/master/README.md`,
54
- `https://raw.githubusercontent.com/${owner}/${repo}/main/skill.md`,
55
- `https://raw.githubusercontent.com/${owner}/${repo}/master/skill.md`,
56
- ];
50
+ async function fetchSkillContent(owner, repo, skillName) {
51
+ // Build possible paths
52
+ const possiblePaths = [];
53
+ if (skillName) {
54
+ // If skill name is provided, try subdirectory paths (skills.sh format)
55
+ const branches = ['main', 'master'];
56
+ for (const branch of branches) {
57
+ possiblePaths.push(`https://raw.githubusercontent.com/${owner}/${repo}/${branch}/skills/${skillName}/SKILL.md`, `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/skills/${skillName}/skill.md`, `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/skills/${skillName}/README.md`);
58
+ }
59
+ }
60
+ // Also try root-level paths
61
+ const branches = ['main', 'master'];
62
+ for (const branch of branches) {
63
+ possiblePaths.push(`https://raw.githubusercontent.com/${owner}/${repo}/${branch}/SKILL.md`, `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/skill.md`, `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/README.md`);
64
+ }
57
65
  for (const url of possiblePaths) {
58
66
  try {
59
67
  const response = await fetch(url);
@@ -66,30 +74,34 @@ async function fetchSkillContent(owner, repo) {
66
74
  // Continue to next URL
67
75
  }
68
76
  }
77
+ const skillHint = skillName ? ` (skill: ${skillName})` : '';
69
78
  return {
70
79
  success: false,
71
- error: `Could not fetch skill from ${owner}/${repo}. Check that the repository exists and is public.`
80
+ error: `Could not fetch skill from ${owner}/${repo}${skillHint}. Check that the repository exists and is public.`
72
81
  };
73
82
  }
74
- function saveSkillFiles(skillDir, skillName, ownerRepo, content) {
83
+ function saveSkillFiles(skillDir, skillName, ownerRepo, content, _originalSkillName) {
75
84
  // Parse frontmatter if present
76
85
  const { frontmatter, body } = parseFrontmatter(content);
77
- // Create skill.md
78
- const skillMd = `# Skill: ${skillName}
79
-
80
- ## Source
81
- Imported from [skills.sh](https://skills.sh): \`${ownerRepo}\`
86
+ // Extract name and description from frontmatter or use defaults
87
+ const skillNameFromFrontmatter = typeof frontmatter['name'] === 'string' ? frontmatter['name'] : skillName;
88
+ const skillDescription = typeof frontmatter['description'] === 'string' ? frontmatter['description'] : `Skill imported from skills.sh: ${ownerRepo}`;
89
+ // Create SKILL.md (skills.sh format) with frontmatter
90
+ const skillMd = `---
91
+ name: ${skillNameFromFrontmatter}
92
+ description: ${skillDescription}
93
+ ---
82
94
 
83
- ## Description
84
95
  ${body || 'No description available.'}
85
96
  `;
86
- writeFileSync(join(skillDir, 'skill.md'), skillMd);
97
+ writeFileSync(join(skillDir, 'SKILL.md'), skillMd);
87
98
  // Create metadata.yaml with source attribution
99
+ // Use frontmatter values if available, otherwise use defaults
88
100
  const version = typeof frontmatter['version'] === 'string' ? frontmatter['version'] : '1.0.0';
89
101
  const description = typeof frontmatter['description'] === 'string' ? frontmatter['description'] : `Skill imported from skills.sh: ${ownerRepo}`;
90
102
  const tags = Array.isArray(frontmatter['tags']) ? frontmatter['tags'] : [];
91
103
  const metadata = {
92
- name: skillName,
104
+ name: skillNameFromFrontmatter,
93
105
  version,
94
106
  description,
95
107
  tags,
@@ -112,7 +124,7 @@ ${body || 'No description available.'}
112
124
  steps: [
113
125
  {
114
126
  type: 'procedural',
115
- description: 'This skill contains procedural knowledge. See skill.md for details.'
127
+ description: 'This skill contains procedural knowledge. See SKILL.md for details.'
116
128
  }
117
129
  ]
118
130
  };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Step executors for automated task and skill execution
3
+ */
4
+ export interface StepResult {
5
+ success: boolean;
6
+ output?: string;
7
+ error?: string;
8
+ exitCode?: number;
9
+ duration: number;
10
+ data?: Record<string, unknown>;
11
+ }
12
+ export interface ExecutionContext {
13
+ taskId: string;
14
+ workingDir: string;
15
+ env?: Record<string, string>;
16
+ dryRun?: boolean;
17
+ }
18
+ export interface Step {
19
+ type: string;
20
+ description?: string;
21
+ [key: string]: unknown;
22
+ }
23
+ export interface StepExecutor {
24
+ type: string;
25
+ execute(step: Step, context: ExecutionContext): Promise<StepResult>;
26
+ }
27
+ /**
28
+ * Bash step executor - executes shell commands
29
+ */
30
+ export declare class BashStepExecutor implements StepExecutor {
31
+ type: string;
32
+ execute(step: Step, context: ExecutionContext): Promise<StepResult>;
33
+ }
34
+ /**
35
+ * File step executor - performs file operations
36
+ */
37
+ export declare class FileStepExecutor implements StepExecutor {
38
+ type: string;
39
+ execute(step: Step, context: ExecutionContext): Promise<StepResult>;
40
+ }
41
+ /**
42
+ * API step executor - makes HTTP requests
43
+ */
44
+ export declare class ApiStepExecutor implements StepExecutor {
45
+ type: string;
46
+ execute(step: Step, context: ExecutionContext): Promise<StepResult>;
47
+ }
48
+ /**
49
+ * Script step executor - executes Node.js scripts
50
+ */
51
+ export declare class ScriptStepExecutor implements StepExecutor {
52
+ type: string;
53
+ execute(step: Step, context: ExecutionContext): Promise<StepResult>;
54
+ }
55
+ export declare function getStepExecutor(type: string): Promise<StepExecutor | undefined>;
56
+ export declare function registerStepExecutor(executor: StepExecutor): void;
57
+ export declare function executeStep(step: Step, context: ExecutionContext): Promise<StepResult>;
58
+ //# sourceMappingURL=step-executor.d.ts.map