agents-templated 2.2.12 → 2.2.14

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 (56) hide show
  1. package/README.md +53 -12
  2. package/bin/cli.js +76 -18
  3. package/index.js +2 -2
  4. package/lib/layout.js +27 -3
  5. package/lib/orchestrator.js +562 -0
  6. package/lib/workflow.js +472 -3
  7. package/package.json +1 -1
  8. package/templates/.claude/agents/README.md +15 -1
  9. package/templates/.claude/agents/architect.md +79 -106
  10. package/templates/.claude/agents/backend-specialist.md +79 -0
  11. package/templates/.claude/agents/build-error-resolver.md +78 -119
  12. package/templates/.claude/agents/code-reviewer.md +79 -116
  13. package/templates/.claude/agents/compatibility-checker.md +79 -79
  14. package/templates/.claude/agents/configuration-validator.md +79 -85
  15. package/templates/.claude/agents/database-migrator.md +79 -83
  16. package/templates/.claude/agents/dependency-auditor.md +79 -92
  17. package/templates/.claude/agents/deployment-specialist.md +91 -0
  18. package/templates/.claude/agents/doc-updater.md +78 -130
  19. package/templates/.claude/agents/e2e-runner.md +78 -122
  20. package/templates/.claude/agents/frontend-specialist.md +79 -0
  21. package/templates/.claude/agents/load-tester.md +79 -80
  22. package/templates/.claude/agents/performance-profiler.md +79 -103
  23. package/templates/.claude/agents/performance-specialist.md +91 -0
  24. package/templates/.claude/agents/planner.md +81 -87
  25. package/templates/.claude/agents/qa-specialist.md +92 -0
  26. package/templates/.claude/agents/refactor-cleaner.md +79 -137
  27. package/templates/.claude/agents/release-ops-specialist.md +80 -0
  28. package/templates/.claude/agents/security-reviewer.md +80 -138
  29. package/templates/.claude/agents/tdd-guide.md +79 -98
  30. package/templates/.claude/agents/test-data-builder.md +79 -0
  31. package/templates/CLAUDE.md +7 -0
  32. package/templates/README.md +37 -9
  33. package/templates/agent-docs/ARCHITECTURE.md +6 -0
  34. package/templates/agents/commands/README.md +84 -4
  35. package/templates/agents/commands/SCHEMA.md +21 -1
  36. package/templates/agents/commands/test-data.md +56 -0
  37. package/agents/commands/README.md +0 -64
  38. package/agents/commands/SCHEMA.md +0 -22
  39. package/agents/commands/arch-check.md +0 -58
  40. package/agents/commands/audit.md +0 -58
  41. package/agents/commands/debug-track.md +0 -58
  42. package/agents/commands/docs.md +0 -58
  43. package/agents/commands/fix.md +0 -58
  44. package/agents/commands/learn-loop.md +0 -58
  45. package/agents/commands/perf.md +0 -58
  46. package/agents/commands/plan.md +0 -58
  47. package/agents/commands/pr.md +0 -58
  48. package/agents/commands/problem-map.md +0 -58
  49. package/agents/commands/release-ready.md +0 -58
  50. package/agents/commands/release.md +0 -58
  51. package/agents/commands/risk-review.md +0 -58
  52. package/agents/commands/scope-shape.md +0 -58
  53. package/agents/commands/task.md +0 -58
  54. package/agents/commands/test.md +0 -58
  55. package/agents/commands/ux-bar.md +0 -58
  56. package/agents/rules/planning.mdc +0 -69
@@ -0,0 +1,562 @@
1
+ const {
2
+ WORKFLOW_COMMANDS,
3
+ ORCHESTRATION_TRACKS,
4
+ OPTIONAL_SUBAGENT_RULES,
5
+ DEPRECATED_SUBAGENT_ALIASES,
6
+ NON_OVERLAP_ROUTE_BOUNDARIES,
7
+ MODE_LOCKED_SPECIALISTS,
8
+ SECURITY_REVIEW_POLICY,
9
+ REFACTOR_BUILD_RETRY_CAP,
10
+ REFACTOR_BUILD_HALT_AFTER,
11
+ findScenarioById,
12
+ resolveScenarioFromObjective
13
+ } = require('./workflow');
14
+
15
+ function createExecutionId(command) {
16
+ return `${command}-${Date.now().toString(36)}`;
17
+ }
18
+
19
+ function normalizeMode(mode) {
20
+ return mode === 'slash-command' ? 'slash-command' : 'slash-command-auto';
21
+ }
22
+
23
+ class WorkflowOrchestrator {
24
+ constructor() {
25
+ this.workflowLookup = new Map(WORKFLOW_COMMANDS.map((workflow) => [workflow.cli, workflow]));
26
+ }
27
+
28
+ resolveScenario({ scenarioId, objective }) {
29
+ if (scenarioId) {
30
+ const explicitScenario = findScenarioById(scenarioId);
31
+ if (!explicitScenario) {
32
+ throw new Error(`Unknown scenario: ${scenarioId}`);
33
+ }
34
+
35
+ return {
36
+ scenario: explicitScenario,
37
+ reason: 'explicit scenario override'
38
+ };
39
+ }
40
+
41
+ return resolveScenarioFromObjective(objective);
42
+ }
43
+
44
+ resolveOptionalSubagents({ scenarioId, phase, objective, routeState }) {
45
+ const objectiveLower = (objective || '').toLowerCase();
46
+ const activeSubagents = routeState.activeSubagents;
47
+
48
+ const matchesRule = (rule) => {
49
+ const when = rule.when || {};
50
+
51
+ if (Array.isArray(when.scenarios) && when.scenarios.length > 0 && !when.scenarios.includes(scenarioId)) {
52
+ return false;
53
+ }
54
+
55
+ if (Array.isArray(when.tracks) && when.tracks.length > 0 && !when.tracks.includes(phase.track)) {
56
+ return false;
57
+ }
58
+
59
+ if (Array.isArray(when.commands) && when.commands.length > 0 && !when.commands.includes(phase.command)) {
60
+ return false;
61
+ }
62
+
63
+ if (Array.isArray(when.keywords) && when.keywords.length > 0) {
64
+ const keywordMatched = when.keywords.some((keyword) => objectiveLower.includes(keyword.toLowerCase()));
65
+ if (!keywordMatched) {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ if (Array.isArray(rule.dependsOnSubagents) && rule.dependsOnSubagents.length > 0) {
71
+ const isDependencySatisfied = rule.dependsOnSubagents.every((name) => activeSubagents.has(name));
72
+ if (!isDependencySatisfied) {
73
+ return false;
74
+ }
75
+ }
76
+
77
+ return true;
78
+ };
79
+
80
+ let optionalSubagents = OPTIONAL_SUBAGENT_RULES
81
+ .filter(matchesRule)
82
+ .map((rule) => {
83
+ const alias = DEPRECATED_SUBAGENT_ALIASES[rule.subagent] || null;
84
+ const delegatedName = alias ? alias.to : rule.subagent;
85
+ const delegatedMode = rule.mode || (alias ? alias.mode : null);
86
+
87
+ if (alias) {
88
+ routeState.deprecationNotices.add(alias.notice);
89
+ }
90
+
91
+ return {
92
+ name: delegatedName,
93
+ required: false,
94
+ invocation_mode: delegatedMode,
95
+ reason: rule.reason || 'Scenario-specific optional delegation.',
96
+ deprecated_from: alias ? rule.subagent : null,
97
+ deprecation_notice: alias ? alias.notice : null
98
+ };
99
+ });
100
+
101
+ const commandAllowlist = NON_OVERLAP_ROUTE_BOUNDARIES.commandAllowlist[phase.command];
102
+ if (Array.isArray(commandAllowlist) && commandAllowlist.length > 0) {
103
+ optionalSubagents = optionalSubagents.filter((entry) => commandAllowlist.includes(entry.name));
104
+ }
105
+
106
+ if (
107
+ phase.command === 'risk-review' &&
108
+ optionalSubagents.some((entry) => entry.name === 'code-reviewer') &&
109
+ !optionalSubagents.some((entry) => entry.name === 'dependency-auditor')
110
+ ) {
111
+ optionalSubagents.push({
112
+ name: 'dependency-auditor',
113
+ required: false,
114
+ invocation_mode: null,
115
+ reason: 'Sequenced after code-reviewer to preserve review/dependency/docs ownership boundaries.',
116
+ deprecated_from: null,
117
+ deprecation_notice: null
118
+ });
119
+ }
120
+
121
+ const mergedByIdentity = new Map();
122
+ for (const entry of optionalSubagents) {
123
+ const identity = `${entry.name}::${entry.invocation_mode || 'none'}`;
124
+ if (!mergedByIdentity.has(identity)) {
125
+ mergedByIdentity.set(identity, entry);
126
+ continue;
127
+ }
128
+
129
+ const existing = mergedByIdentity.get(identity);
130
+ const reasonParts = [existing.reason, entry.reason].filter(Boolean);
131
+ existing.reason = Array.from(new Set(reasonParts)).join(' ');
132
+ existing.required = existing.required || entry.required;
133
+ mergedByIdentity.set(identity, existing);
134
+ }
135
+
136
+ const reviewOrder = NON_OVERLAP_ROUTE_BOUNDARIES.reviewSequence;
137
+ return Array.from(mergedByIdentity.values()).sort((left, right) => {
138
+ const leftIndex = reviewOrder.indexOf(left.name);
139
+ const rightIndex = reviewOrder.indexOf(right.name);
140
+
141
+ if (leftIndex === -1 && rightIndex === -1) {
142
+ return left.name.localeCompare(right.name);
143
+ }
144
+ if (leftIndex === -1) {
145
+ return 1;
146
+ }
147
+ if (rightIndex === -1) {
148
+ return -1;
149
+ }
150
+ return leftIndex - rightIndex;
151
+ });
152
+ }
153
+
154
+ resolveInvocationMode({ routedSubagent, phase, objective, routeState }) {
155
+ const objectiveLower = (objective || '').toLowerCase();
156
+
157
+ if (routedSubagent === 'qa-specialist') {
158
+ if (phase.track === 'qa-design') {
159
+ return 'design';
160
+ }
161
+ if (phase.track === 'qa') {
162
+ return 'validation';
163
+ }
164
+ return null;
165
+ }
166
+
167
+ if (routedSubagent === 'performance-specialist') {
168
+ if (routeState.activeSubagents.has('test-data-builder')) {
169
+ return 'load';
170
+ }
171
+
172
+ const loadKeywords = ['load', 'throughput', 'stress', 'traffic'];
173
+ return loadKeywords.some((keyword) => objectiveLower.includes(keyword)) ? 'load' : 'profile';
174
+ }
175
+
176
+ return null;
177
+ }
178
+
179
+ assertModeLock(subagent, invocationMode) {
180
+ const allowedModes = MODE_LOCKED_SPECIALISTS[subagent];
181
+ if (!allowedModes) {
182
+ return;
183
+ }
184
+
185
+ if (!invocationMode) {
186
+ const error = new Error(`Missing required mode for ${subagent}. Allowed: ${allowedModes.join('|')}`);
187
+ error.name = 'ModeLockError';
188
+ throw error;
189
+ }
190
+
191
+ if (!allowedModes.includes(invocationMode)) {
192
+ const error = new Error(`Unsupported mode ${invocationMode} for ${subagent}. Allowed: ${allowedModes.join('|')}`);
193
+ error.name = 'ModeLockError';
194
+ throw error;
195
+ }
196
+ }
197
+
198
+ evaluateSecurityInvocationPolicy({ objective }) {
199
+ const objectiveLower = (objective || '').toLowerCase();
200
+ const mandatoryMatches = SECURITY_REVIEW_POLICY.mandatoryKeywords
201
+ .filter((keyword) => objectiveLower.includes(keyword));
202
+ const mediumMatches = SECURITY_REVIEW_POLICY.mediumKeywords
203
+ .filter((keyword) => objectiveLower.includes(keyword));
204
+
205
+ if (mandatoryMatches.length > 0) {
206
+ return {
207
+ required: true,
208
+ level: 'mandatory',
209
+ reason: `mandatory security trigger matched: ${mandatoryMatches.join(', ')}`,
210
+ mediumScore: mediumMatches.length,
211
+ skipReason: null
212
+ };
213
+ }
214
+
215
+ if (mediumMatches.length >= SECURITY_REVIEW_POLICY.mediumThreshold) {
216
+ return {
217
+ required: false,
218
+ level: 'optional',
219
+ reason: `optional security trigger score ${mediumMatches.length}/${SECURITY_REVIEW_POLICY.mediumThreshold}`,
220
+ mediumScore: mediumMatches.length,
221
+ skipReason: null
222
+ };
223
+ }
224
+
225
+ return {
226
+ required: false,
227
+ level: 'skipped',
228
+ reason: null,
229
+ mediumScore: mediumMatches.length,
230
+ skipReason: `no mandatory trigger and medium score ${mediumMatches.length}/${SECURITY_REVIEW_POLICY.mediumThreshold}`
231
+ };
232
+ }
233
+
234
+ evaluateRefactorRepairPolicy({ objective, retryCycle }) {
235
+ const objectiveLower = (objective || '').toLowerCase();
236
+ const refactorDetected = ['refactor', 'cleanup', 'dead code', 'unused'].some((keyword) => objectiveLower.includes(keyword));
237
+
238
+ const normalizedRetryCycle = Number.isInteger(retryCycle) ? retryCycle : 0;
239
+ if (normalizedRetryCycle >= REFACTOR_BUILD_HALT_AFTER) {
240
+ const error = new Error(
241
+ `Refactor-repair retry cap exceeded at cycle ${normalizedRetryCycle}. Allowed max cycles: ${REFACTOR_BUILD_RETRY_CAP}`
242
+ );
243
+ error.name = 'RetryCapError';
244
+ throw error;
245
+ }
246
+
247
+ return {
248
+ active: refactorDetected,
249
+ retryCycle: normalizedRetryCycle,
250
+ retryCap: REFACTOR_BUILD_RETRY_CAP,
251
+ haltAfter: REFACTOR_BUILD_HALT_AFTER
252
+ };
253
+ }
254
+
255
+ buildPhases(scenario, objective, securityPolicy) {
256
+ let securityInjected = false;
257
+ const routeState = {
258
+ activeSubagents: new Set(),
259
+ deprecationNotices: new Set()
260
+ };
261
+ const phases = [];
262
+
263
+ for (let index = 0; index < scenario.phases.length; index += 1) {
264
+ const phase = scenario.phases[index];
265
+ const workflow = this.workflowLookup.get(phase.command);
266
+ const trackProfile = ORCHESTRATION_TRACKS[phase.track] || null;
267
+ const optionalSubagents = this.resolveOptionalSubagents({
268
+ scenarioId: scenario.id,
269
+ phase,
270
+ objective,
271
+ routeState
272
+ });
273
+ const routedSubagent = trackProfile ? trackProfile.subagent : null;
274
+ const invocationMode = this.resolveInvocationMode({
275
+ routedSubagent,
276
+ phase,
277
+ objective,
278
+ routeState
279
+ });
280
+
281
+ this.assertModeLock(routedSubagent, invocationMode);
282
+
283
+ if (
284
+ !securityInjected &&
285
+ securityPolicy.required &&
286
+ ['backend', 'frontend', 'release-ops', 'deployment'].includes(phase.track)
287
+ ) {
288
+ optionalSubagents.push({
289
+ name: 'security-reviewer',
290
+ required: true,
291
+ invocation_mode: null,
292
+ reason: securityPolicy.reason
293
+ });
294
+ securityInjected = true;
295
+ }
296
+
297
+ for (const optionalSubagent of optionalSubagents) {
298
+ this.assertModeLock(optionalSubagent.name, optionalSubagent.invocation_mode);
299
+ }
300
+
301
+ const handoff_inputs = [];
302
+ if (routeState.activeSubagents.has('test-data-builder') && ['qa', 'frontend', 'performance'].includes(phase.track)) {
303
+ handoff_inputs.push('test-data-builder');
304
+ }
305
+
306
+ const phaseResult = {
307
+ phase_id: `phase-${index + 1}`,
308
+ orchestration_phase: phase.command,
309
+ command: workflow.cli,
310
+ slash_command: workflow.slash,
311
+ purpose: workflow.purpose,
312
+ specialist: workflow.specialist,
313
+ routed_track: phase.track,
314
+ routed_subagent: routedSubagent,
315
+ invocation_mode: invocationMode,
316
+ handoff_inputs,
317
+ routed_skills: trackProfile ? trackProfile.skills : [],
318
+ optional_subagents: optionalSubagents,
319
+ status: 'completed',
320
+ details: trackProfile ? trackProfile.goal : null
321
+ };
322
+
323
+ phases.push(phaseResult);
324
+
325
+ if (routedSubagent) {
326
+ routeState.activeSubagents.add(routedSubagent);
327
+ }
328
+ for (const optionalSubagent of optionalSubagents) {
329
+ routeState.activeSubagents.add(optionalSubagent.name);
330
+ }
331
+ }
332
+
333
+ return {
334
+ phases,
335
+ deprecationNotices: Array.from(routeState.deprecationNotices)
336
+ };
337
+ }
338
+
339
+ orchestrate({ objective, scenarioId, mode, retryCycle = 0 }) {
340
+ const trimmedObjective = (objective || '').trim();
341
+ const selectedMode = normalizeMode(mode);
342
+
343
+ if (!trimmedObjective) {
344
+ return {
345
+ command: '/orchestrate',
346
+ execution_id: createExecutionId('orchestrate'),
347
+ mode: selectedMode,
348
+ status: 'blocked',
349
+ inputs: {
350
+ objective: objective || null,
351
+ scenario: scenarioId || null
352
+ },
353
+ prechecks: [
354
+ {
355
+ name: 'objective_non_empty',
356
+ status: 'failed',
357
+ details: 'Provide a non-empty orchestration objective.'
358
+ }
359
+ ],
360
+ execution_log: [],
361
+ artifacts: null,
362
+ risks: [
363
+ {
364
+ level: 'high',
365
+ message: 'Orchestration was blocked because objective is missing.'
366
+ }
367
+ ],
368
+ safety_checks: [
369
+ {
370
+ name: 'scope_guard',
371
+ status: 'passed',
372
+ details: 'No execution started.'
373
+ }
374
+ ],
375
+ stop_condition: 'objective is required',
376
+ next_action: 'Provide an objective and rerun /orchestrate.'
377
+ };
378
+ }
379
+
380
+ try {
381
+ const refactorPolicy = this.evaluateRefactorRepairPolicy({
382
+ objective: trimmedObjective,
383
+ retryCycle
384
+ });
385
+ const { scenario, reason } = this.resolveScenario({ scenarioId, objective: trimmedObjective });
386
+ const securityPolicy = this.evaluateSecurityInvocationPolicy({
387
+ objective: trimmedObjective
388
+ });
389
+ const buildResult = this.buildPhases(scenario, trimmedObjective, securityPolicy);
390
+ const phases = buildResult.phases;
391
+ const executionLog = phases.map((phase) => ({
392
+ phase: phase.phase_id,
393
+ orchestration_phase: phase.orchestration_phase,
394
+ routed_subagent: phase.routed_subagent,
395
+ invocation_mode: phase.invocation_mode,
396
+ handoff_inputs: phase.handoff_inputs,
397
+ routed_track: phase.routed_track,
398
+ optional_subagents: phase.optional_subagents,
399
+ status: 'delegated',
400
+ details: `Delegated ${phase.command} to ${phase.routed_subagent}`
401
+ }));
402
+
403
+ const risks = [];
404
+ if (!scenarioId) {
405
+ risks.push({
406
+ level: 'low',
407
+ message: `Scenario auto-selection: ${reason}.`
408
+ });
409
+ }
410
+
411
+ return {
412
+ command: '/orchestrate',
413
+ execution_id: createExecutionId('orchestrate'),
414
+ mode: selectedMode,
415
+ status: 'completed',
416
+ inputs: {
417
+ objective: trimmedObjective,
418
+ scenario: scenario.id
419
+ },
420
+ prechecks: [
421
+ {
422
+ name: 'objective_non_empty',
423
+ status: 'passed',
424
+ details: null
425
+ }
426
+ ],
427
+ execution_log: executionLog,
428
+ artifacts: {
429
+ scenario: scenario.id,
430
+ scenario_reason: reason,
431
+ security_policy: securityPolicy,
432
+ refactor_repair_policy: refactorPolicy,
433
+ deprecation_notices: buildResult.deprecationNotices,
434
+ orchestration_summary: `Generated ${phases.length} phase handoffs for ${scenario.id}.`,
435
+ phases
436
+ },
437
+ risks,
438
+ safety_checks: [
439
+ {
440
+ name: 'scope_guard',
441
+ status: 'passed',
442
+ details: 'No hidden scope expansion detected in selected scenario.'
443
+ }
444
+ ],
445
+ stop_condition: null,
446
+ next_action: null
447
+ };
448
+ } catch (error) {
449
+ const blockedByPolicy = error.name === 'ModeLockError' || error.name === 'RetryCapError';
450
+
451
+ return {
452
+ command: '/orchestrate',
453
+ execution_id: createExecutionId('orchestrate'),
454
+ mode: selectedMode,
455
+ status: blockedByPolicy ? 'blocked' : 'failed',
456
+ inputs: {
457
+ objective: trimmedObjective,
458
+ scenario: scenarioId || null
459
+ },
460
+ prechecks: [
461
+ {
462
+ name: 'objective_non_empty',
463
+ status: 'passed',
464
+ details: null
465
+ }
466
+ ],
467
+ execution_log: [],
468
+ artifacts: null,
469
+ risks: [
470
+ {
471
+ level: 'high',
472
+ message: error.message
473
+ }
474
+ ],
475
+ safety_checks: [
476
+ {
477
+ name: 'scope_guard',
478
+ status: 'passed',
479
+ details: 'No mutation occurred.'
480
+ }
481
+ ],
482
+ stop_condition: error.message,
483
+ next_action: 'Choose a valid scenario id or omit --scenario to use auto-routing.'
484
+ };
485
+ }
486
+ }
487
+ }
488
+
489
+ function formatOrchestrationOutput(result) {
490
+ const lines = [];
491
+ lines.push('');
492
+ lines.push('Orchestration run');
493
+ lines.push(` Command: ${result.command}`);
494
+ lines.push(` Execution id: ${result.execution_id}`);
495
+ lines.push(` Mode: ${result.mode}`);
496
+ lines.push(` Status: ${result.status}`);
497
+
498
+ if (result.inputs) {
499
+ lines.push(` Objective: ${result.inputs.objective || 'n/a'}`);
500
+ lines.push(` Scenario: ${result.inputs.scenario || 'n/a'}`);
501
+ }
502
+
503
+ if (result.artifacts && Array.isArray(result.artifacts.phases)) {
504
+ lines.push('');
505
+ lines.push('Auto-routed phases');
506
+ for (const phase of result.artifacts.phases) {
507
+ const modeText = phase.invocation_mode ? ` [mode=${phase.invocation_mode}]` : '';
508
+ lines.push(` - ${phase.command} (${phase.routed_track}) -> ${phase.routed_subagent}${modeText}`);
509
+
510
+ if (Array.isArray(phase.handoff_inputs) && phase.handoff_inputs.length > 0) {
511
+ lines.push(` handoff_inputs: ${phase.handoff_inputs.join(', ')}`);
512
+ }
513
+
514
+ if (Array.isArray(phase.optional_subagents) && phase.optional_subagents.length > 0) {
515
+ lines.push(` optional: ${phase.optional_subagents.map((entry) => entry.name).join(', ')}`);
516
+ }
517
+ }
518
+ }
519
+
520
+ if (result.artifacts && Array.isArray(result.artifacts.deprecation_notices) && result.artifacts.deprecation_notices.length > 0) {
521
+ lines.push('');
522
+ lines.push('Deprecation notices');
523
+ for (const notice of result.artifacts.deprecation_notices) {
524
+ lines.push(` - ${notice}`);
525
+ }
526
+ }
527
+
528
+ if (result.artifacts && result.artifacts.security_policy) {
529
+ const securityPolicy = result.artifacts.security_policy;
530
+ lines.push('');
531
+ lines.push(`Security policy: ${securityPolicy.level}`);
532
+ if (securityPolicy.reason) {
533
+ lines.push(` reason: ${securityPolicy.reason}`);
534
+ }
535
+ if (securityPolicy.skipReason) {
536
+ lines.push(` skip: ${securityPolicy.skipReason}`);
537
+ }
538
+ }
539
+
540
+ if (result.artifacts && result.artifacts.refactor_repair_policy) {
541
+ const retryPolicy = result.artifacts.refactor_repair_policy;
542
+ lines.push('');
543
+ lines.push(`Refactor repair policy: retry_cycle=${retryPolicy.retryCycle}, cap=${retryPolicy.retryCap}, halt_after=${retryPolicy.haltAfter}`);
544
+ }
545
+
546
+ if (result.stop_condition) {
547
+ lines.push('');
548
+ lines.push(`Stop condition: ${result.stop_condition}`);
549
+ }
550
+
551
+ if (result.next_action) {
552
+ lines.push(`Next action: ${result.next_action}`);
553
+ }
554
+
555
+ lines.push('');
556
+ return `${lines.join('\n')}\n`;
557
+ }
558
+
559
+ module.exports = {
560
+ WorkflowOrchestrator,
561
+ formatOrchestrationOutput
562
+ };