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.
- package/README.md +53 -12
- package/bin/cli.js +76 -18
- package/index.js +2 -2
- package/lib/layout.js +27 -3
- package/lib/orchestrator.js +562 -0
- package/lib/workflow.js +472 -3
- package/package.json +1 -1
- package/templates/.claude/agents/README.md +15 -1
- package/templates/.claude/agents/architect.md +79 -106
- package/templates/.claude/agents/backend-specialist.md +79 -0
- package/templates/.claude/agents/build-error-resolver.md +78 -119
- package/templates/.claude/agents/code-reviewer.md +79 -116
- package/templates/.claude/agents/compatibility-checker.md +79 -79
- package/templates/.claude/agents/configuration-validator.md +79 -85
- package/templates/.claude/agents/database-migrator.md +79 -83
- package/templates/.claude/agents/dependency-auditor.md +79 -92
- package/templates/.claude/agents/deployment-specialist.md +91 -0
- package/templates/.claude/agents/doc-updater.md +78 -130
- package/templates/.claude/agents/e2e-runner.md +78 -122
- package/templates/.claude/agents/frontend-specialist.md +79 -0
- package/templates/.claude/agents/load-tester.md +79 -80
- package/templates/.claude/agents/performance-profiler.md +79 -103
- package/templates/.claude/agents/performance-specialist.md +91 -0
- package/templates/.claude/agents/planner.md +81 -87
- package/templates/.claude/agents/qa-specialist.md +92 -0
- package/templates/.claude/agents/refactor-cleaner.md +79 -137
- package/templates/.claude/agents/release-ops-specialist.md +80 -0
- package/templates/.claude/agents/security-reviewer.md +80 -138
- package/templates/.claude/agents/tdd-guide.md +79 -98
- package/templates/.claude/agents/test-data-builder.md +79 -0
- package/templates/CLAUDE.md +7 -0
- package/templates/README.md +37 -9
- package/templates/agent-docs/ARCHITECTURE.md +6 -0
- package/templates/agents/commands/README.md +84 -4
- package/templates/agents/commands/SCHEMA.md +21 -1
- package/templates/agents/commands/test-data.md +56 -0
- package/agents/commands/README.md +0 -64
- package/agents/commands/SCHEMA.md +0 -22
- package/agents/commands/arch-check.md +0 -58
- package/agents/commands/audit.md +0 -58
- package/agents/commands/debug-track.md +0 -58
- package/agents/commands/docs.md +0 -58
- package/agents/commands/fix.md +0 -58
- package/agents/commands/learn-loop.md +0 -58
- package/agents/commands/perf.md +0 -58
- package/agents/commands/plan.md +0 -58
- package/agents/commands/pr.md +0 -58
- package/agents/commands/problem-map.md +0 -58
- package/agents/commands/release-ready.md +0 -58
- package/agents/commands/release.md +0 -58
- package/agents/commands/risk-review.md +0 -58
- package/agents/commands/scope-shape.md +0 -58
- package/agents/commands/task.md +0 -58
- package/agents/commands/test.md +0 -58
- package/agents/commands/ux-bar.md +0 -58
- 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
|
+
};
|