agents-templated 2.2.12 → 2.2.13

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 (54) hide show
  1. package/README.md +32 -5
  2. package/bin/cli.js +49 -0
  3. package/lib/orchestrator.js +562 -0
  4. package/lib/workflow.js +470 -1
  5. package/package.json +1 -1
  6. package/templates/.claude/agents/README.md +15 -1
  7. package/templates/.claude/agents/architect.md +79 -106
  8. package/templates/.claude/agents/backend-specialist.md +79 -0
  9. package/templates/.claude/agents/build-error-resolver.md +78 -119
  10. package/templates/.claude/agents/code-reviewer.md +79 -116
  11. package/templates/.claude/agents/compatibility-checker.md +79 -79
  12. package/templates/.claude/agents/configuration-validator.md +79 -85
  13. package/templates/.claude/agents/database-migrator.md +79 -83
  14. package/templates/.claude/agents/dependency-auditor.md +79 -92
  15. package/templates/.claude/agents/deployment-specialist.md +91 -0
  16. package/templates/.claude/agents/doc-updater.md +78 -130
  17. package/templates/.claude/agents/e2e-runner.md +78 -122
  18. package/templates/.claude/agents/frontend-specialist.md +79 -0
  19. package/templates/.claude/agents/load-tester.md +79 -80
  20. package/templates/.claude/agents/performance-profiler.md +79 -103
  21. package/templates/.claude/agents/performance-specialist.md +91 -0
  22. package/templates/.claude/agents/planner.md +81 -87
  23. package/templates/.claude/agents/qa-specialist.md +92 -0
  24. package/templates/.claude/agents/refactor-cleaner.md +79 -137
  25. package/templates/.claude/agents/release-ops-specialist.md +80 -0
  26. package/templates/.claude/agents/security-reviewer.md +80 -138
  27. package/templates/.claude/agents/tdd-guide.md +79 -98
  28. package/templates/.claude/agents/test-data-builder.md +79 -0
  29. package/templates/CLAUDE.md +7 -0
  30. package/templates/README.md +34 -5
  31. package/templates/agent-docs/ARCHITECTURE.md +6 -0
  32. package/templates/agents/commands/README.md +79 -0
  33. package/templates/agents/commands/SCHEMA.md +21 -1
  34. package/templates/agents/commands/test-data.md +56 -0
  35. package/agents/commands/README.md +0 -64
  36. package/agents/commands/SCHEMA.md +0 -22
  37. package/agents/commands/arch-check.md +0 -58
  38. package/agents/commands/audit.md +0 -58
  39. package/agents/commands/debug-track.md +0 -58
  40. package/agents/commands/docs.md +0 -58
  41. package/agents/commands/fix.md +0 -58
  42. package/agents/commands/learn-loop.md +0 -58
  43. package/agents/commands/perf.md +0 -58
  44. package/agents/commands/plan.md +0 -58
  45. package/agents/commands/pr.md +0 -58
  46. package/agents/commands/problem-map.md +0 -58
  47. package/agents/commands/release-ready.md +0 -58
  48. package/agents/commands/release.md +0 -58
  49. package/agents/commands/risk-review.md +0 -58
  50. package/agents/commands/scope-shape.md +0 -58
  51. package/agents/commands/task.md +0 -58
  52. package/agents/commands/test.md +0 -58
  53. package/agents/commands/ux-bar.md +0 -58
  54. package/agents/rules/planning.mdc +0 -69
package/README.md CHANGED
@@ -237,6 +237,7 @@ These commands provide deterministic specialist guidance aligned to the sprint l
237
237
  | `arch-check` | Architecture Reviewer | Lock architecture and edge-case coverage |
238
238
  | `ux-bar` | Design Quality Lead | Raise UX quality before implementation |
239
239
  | `debug-track` | Root-Cause Investigator | Reproduce and isolate root cause |
240
+ | `test-data` | Test Data Builder | Prepare deterministic fixtures/seeds for downstream validation |
240
241
  | `risk-review` | Release Risk Reviewer | Surface production-risk issues before merge |
241
242
  | `perf` | Performance Analyst | Optimize performance and guard against regressions |
242
243
  | `release-ready` | Release Coordinator | Prepare release artifacts and final checks |
@@ -245,6 +246,23 @@ These commands provide deterministic specialist guidance aligned to the sprint l
245
246
 
246
247
  Each command maps to deterministic contract files in `agents/commands/` and uses the schema in `agents/commands/SCHEMA.md`.
247
248
 
249
+ ### Deprecated Workflow Aliases
250
+
251
+ The CLI keeps selected legacy names as non-breaking redirects with deterministic notices.
252
+
253
+ | Deprecated | Canonical |
254
+ |------------|-----------|
255
+ | `quality-gate` | `risk-review` |
256
+ | `perf-scan` | `perf` |
257
+ | `docs-sync` | `docs` |
258
+
259
+ Migration guidance:
260
+
261
+ - Existing scripts continue to work.
262
+ - Alias invocations print a deprecation warning and redirect deterministically.
263
+ - New automation should use canonical names only.
264
+ - Sunset guidance: deprecated aliases remain supported through v2.x and are scheduled for removal in v3.0.
265
+
248
266
 
249
267
  ---
250
268
 
@@ -361,11 +379,20 @@ Your AI will follow the enterprise patterns automatically!
361
379
 
362
380
  | Agent | Responsibility |
363
381
  |-------|---------------|
364
- | **FrontendAgent** | UI/UX, components, design system, accessibility |
365
- | **BackendAgent** | API, business logic, authentication, middleware |
366
- | **DatabaseAgent** | Schema design, migrations, query optimization |
367
- | **TestAgent** | Unit, integration, E2E, accessibility testing |
368
- | **SecurityAgent** | Input validation, authentication, OWASP compliance |
382
+ | **backend-specialist** | API, business logic, auth middleware, persistence changes |
383
+ | **frontend-specialist** | UI/UX implementation, accessibility, interaction behavior |
384
+ | **qa-specialist** | Design-mode test planning and validation-mode regression gates |
385
+ | **performance-specialist** | Mode-locked performance profiling and load threshold validation |
386
+ | **test-data-builder** | Deterministic fixtures, seeds, and downstream handoff contracts |
387
+ | **security-reviewer** | Conditional security invocation based on trigger thresholds |
388
+ | **dependency-auditor** | CVE/dependency risk auditing and upgrade hygiene |
389
+ | **deployment-specialist** | Ordered deployment phase contract and rollback readiness |
390
+
391
+ #### Separation-Preservation Sequence Contracts
392
+
393
+ - Backend implementation lane: `backend-specialist -> build-error-resolver -> compatibility-checker`
394
+ - Review/governance lane: `code-reviewer -> dependency-auditor -> doc-updater`
395
+ - Test-data handoff lane: `qa-specialist(mode=design) -> test-data-builder -> qa-specialist(mode=validation) -> e2e-runner -> performance-specialist(mode=load)`
369
396
 
370
397
  **Reference**: [AGENTS.MD](AGENTS.MD)
371
398
 
package/bin/cli.js CHANGED
@@ -27,9 +27,14 @@ const {
27
27
  WORKFLOW_COMMANDS,
28
28
  CONTRACT_FILES,
29
29
  SPECIALIST_CONTRACT_FILES,
30
+ DEPRECATED_COMMAND_ALIASES,
30
31
  formatWorkflowOutput,
31
32
  validateWorkflowDefinitions
32
33
  } = require('../lib/workflow');
34
+ const {
35
+ WorkflowOrchestrator,
36
+ formatOrchestrationOutput
37
+ } = require('../lib/orchestrator');
33
38
 
34
39
  // Resolve the templates directory - works in both dev and installed contexts
35
40
  const getTemplatesDir = () => {
@@ -1109,6 +1114,32 @@ program
1109
1114
  console.log(chalk.white(' agents-templated problem-map "daily briefing app for founders"\n'));
1110
1115
  });
1111
1116
 
1117
+ program
1118
+ .command('orchestrate [objective...]')
1119
+ .description('Automatically route a high-level objective across specialist subagents')
1120
+ .option('--scenario <id>', 'Force a specific scenario id (feature-delivery, backend-api, frontend-feature, bug-fix, deployment)')
1121
+ .option('--mode <mode>', 'slash-command or slash-command-auto', 'slash-command-auto')
1122
+ .option('--retry-cycle <n>', 'Current retry cycle for refactor-cleaner/build-error-resolver loop control', '0')
1123
+ .option('--json', 'Emit structured JSON only')
1124
+ .action((objective, options) => {
1125
+ const objectiveText = Array.isArray(objective) ? objective.join(' ') : '';
1126
+ const retryCycle = Number.parseInt(options.retryCycle, 10);
1127
+ const orchestrator = new WorkflowOrchestrator();
1128
+ const result = orchestrator.orchestrate({
1129
+ objective: objectiveText,
1130
+ scenarioId: options.scenario,
1131
+ mode: options.mode,
1132
+ retryCycle: Number.isNaN(retryCycle) ? 0 : retryCycle
1133
+ });
1134
+
1135
+ if (options.json) {
1136
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
1137
+ return;
1138
+ }
1139
+
1140
+ process.stdout.write(formatOrchestrationOutput(result));
1141
+ });
1142
+
1112
1143
  for (const workflow of WORKFLOW_COMMANDS) {
1113
1144
  program
1114
1145
  .command(`${workflow.cli} [objective...]`)
@@ -1119,6 +1150,24 @@ for (const workflow of WORKFLOW_COMMANDS) {
1119
1150
  });
1120
1151
  }
1121
1152
 
1153
+ for (const deprecatedAlias of DEPRECATED_COMMAND_ALIASES) {
1154
+ program
1155
+ .command(`${deprecatedAlias.from} [objective...]`)
1156
+ .description(`Deprecated alias for ${deprecatedAlias.to}`)
1157
+ .action((objective) => {
1158
+ const objectiveText = Array.isArray(objective) ? objective.join(' ') : '';
1159
+ const redirectedWorkflow = WORKFLOW_COMMANDS.find((workflow) => workflow.cli === deprecatedAlias.to);
1160
+
1161
+ if (!redirectedWorkflow) {
1162
+ console.error(chalk.red(`Deprecated alias target not found: ${deprecatedAlias.to}`));
1163
+ process.exit(1);
1164
+ }
1165
+
1166
+ console.log(chalk.yellow(`[deprecated] ${deprecatedAlias.notice}`));
1167
+ process.stdout.write(formatWorkflowOutput(redirectedWorkflow, objectiveText));
1168
+ });
1169
+ }
1170
+
1122
1171
  program
1123
1172
  .command('new-skill <name>')
1124
1173
  .description('Scaffold a new skill in .github/skills/<name>/SKILL.md')
@@ -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
+ };