swarm-orchestrator 7.0.0-alpha.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.
- package/LICENSE +15 -0
- package/README.md +199 -0
- package/agents/reviewer.agent.md +50 -0
- package/agents/worker.agent.md +41 -0
- package/config/default-agents.yaml +56 -0
- package/config/pm-agent.yaml +36 -0
- package/config/quality-gates.yaml +88 -0
- package/config/repair-agent.yaml +34 -0
- package/config/semgrep-rules/complexity-mismatch.yaml +6 -0
- package/config/semgrep-rules/exception-swallowing.yaml +13 -0
- package/config/semgrep-rules/hardcoded-answer.yaml +6 -0
- package/config/semgrep-rules/mock-mutation.yaml +6 -0
- package/config/semgrep-rules/test-modification.yaml +6 -0
- package/config/user-agents.yaml +23 -0
- package/dist/src/adapters/adapter-factory.js +29 -0
- package/dist/src/adapters/agent-adapter.js +32 -0
- package/dist/src/adapters/claude-code-adapter.js +126 -0
- package/dist/src/adapters/claude-code-teams.js +119 -0
- package/dist/src/adapters/codex-adapter.js +118 -0
- package/dist/src/adapters/copilot-adapter.js +224 -0
- package/dist/src/adapters/fatal-error-classifier.js +88 -0
- package/dist/src/adapters/index.js +44 -0
- package/dist/src/adapters/persistent-session.js +204 -0
- package/dist/src/adapters/process-supervisor.js +199 -0
- package/dist/src/agents-exporter.js +398 -0
- package/dist/src/analytics-log.js +161 -0
- package/dist/src/baseline-scanner.js +65 -0
- package/dist/src/bootstrap-evidence.js +88 -0
- package/dist/src/bootstrap-orchestrator.js +205 -0
- package/dist/src/bootstrap-types.js +5 -0
- package/dist/src/branch-merger.js +427 -0
- package/dist/src/cli/attest-handlers.js +44 -0
- package/dist/src/cli/cost-prompt.js +51 -0
- package/dist/src/cli/demo-handlers.js +274 -0
- package/dist/src/cli/flags.js +152 -0
- package/dist/src/cli/index.js +57 -0
- package/dist/src/cli/live-status.js +186 -0
- package/dist/src/cli/misc-handlers.js +242 -0
- package/dist/src/cli/plan-handlers.js +350 -0
- package/dist/src/cli/share-handlers.js +163 -0
- package/dist/src/cli/status-handlers.js +457 -0
- package/dist/src/cli/swarm-handlers.js +638 -0
- package/dist/src/cli/usage.js +72 -0
- package/dist/src/cli.js +217 -0
- package/dist/src/commit-pattern-detector.js +143 -0
- package/dist/src/commit-quality-analyzer.js +38 -0
- package/dist/src/config-loader.js +339 -0
- package/dist/src/context-broker.js +358 -0
- package/dist/src/copilot-cli-wrapper.js +258 -0
- package/dist/src/cost-estimator.js +241 -0
- package/dist/src/defaults.js +18 -0
- package/dist/src/demo-mode.js +180 -0
- package/dist/src/deployment-handler.js +78 -0
- package/dist/src/deployment-manager.js +228 -0
- package/dist/src/execution-queue.js +196 -0
- package/dist/src/external-tool-manager.js +184 -0
- package/dist/src/gate-prompt-builder.js +117 -0
- package/dist/src/gate-remediation.js +173 -0
- package/dist/src/github-issues-ingester.js +152 -0
- package/dist/src/github-mcp-integrator.js +164 -0
- package/dist/src/hook-generator.js +229 -0
- package/dist/src/index.js +48 -0
- package/dist/src/knowledge-base.js +300 -0
- package/dist/src/logger.js +111 -0
- package/dist/src/meta-analyzer.js +208 -0
- package/dist/src/metrics-collector.js +209 -0
- package/dist/src/metrics-types.js +5 -0
- package/dist/src/multi-repo-coordinator.js +179 -0
- package/dist/src/orchestrator/async-meta-analysis.js +84 -0
- package/dist/src/orchestrator/fatal-run-error.js +43 -0
- package/dist/src/orchestrator/final-gates-remediation.js +258 -0
- package/dist/src/orchestrator/git-state-utils.js +119 -0
- package/dist/src/orchestrator/pause-controller.js +55 -0
- package/dist/src/orchestrator/replan-runner.js +258 -0
- package/dist/src/orchestrator/step-executor.js +391 -0
- package/dist/src/orchestrator/wave-scheduler-loop.js +204 -0
- package/dist/src/owasp-mapper.js +153 -0
- package/dist/src/owasp-report-renderer.js +39 -0
- package/dist/src/plan-files.js +142 -0
- package/dist/src/plan-generator.js +1207 -0
- package/dist/src/pm-agent.js +296 -0
- package/dist/src/post-run-reporter.js +203 -0
- package/dist/src/pr-automation.js +103 -0
- package/dist/src/pr-manager.js +318 -0
- package/dist/src/presenter/index.js +209 -0
- package/dist/src/prompt-builder.js +173 -0
- package/dist/src/quality-gates/config-loader.js +196 -0
- package/dist/src/quality-gates/default-config.js +243 -0
- package/dist/src/quality-gates/file-utils.js +152 -0
- package/dist/src/quality-gates/gate-runner.js +171 -0
- package/dist/src/quality-gates/gates/accessibility.js +355 -0
- package/dist/src/quality-gates/gates/duplicate-blocks.js +144 -0
- package/dist/src/quality-gates/gates/hardcoded-config.js +52 -0
- package/dist/src/quality-gates/gates/readme-claims.js +62 -0
- package/dist/src/quality-gates/gates/runtime-checks.js +265 -0
- package/dist/src/quality-gates/gates/scaffold-defaults.js +108 -0
- package/dist/src/quality-gates/gates/test-coverage.js +240 -0
- package/dist/src/quality-gates/gates/test-file-protection.js +85 -0
- package/dist/src/quality-gates/gates/test-isolation.js +74 -0
- package/dist/src/quality-gates/index.js +17 -0
- package/dist/src/quality-gates/registry.js +205 -0
- package/dist/src/quality-gates/types.js +2 -0
- package/dist/src/quick-fix-mode.js +302 -0
- package/dist/src/recipe-loader.js +122 -0
- package/dist/src/repair-agent.js +396 -0
- package/dist/src/repo-analyzer.js +342 -0
- package/dist/src/report-generator.js +146 -0
- package/dist/src/report-renderer.js +90 -0
- package/dist/src/requirement-filter.js +52 -0
- package/dist/src/sarif-formatter.js +140 -0
- package/dist/src/scheduling/dependency-analyzer.js +178 -0
- package/dist/src/scheduling/work-stealing-queue.js +123 -0
- package/dist/src/secret-redactor.js +90 -0
- package/dist/src/session-executor.js +587 -0
- package/dist/src/session-manager.js +221 -0
- package/dist/src/share/transcript-verification.js +128 -0
- package/dist/src/share-parser.js +477 -0
- package/dist/src/spinner.js +146 -0
- package/dist/src/step-runner.js +365 -0
- package/dist/src/swarm-orchestrator.js +686 -0
- package/dist/src/task-classifier.js +134 -0
- package/dist/src/test-command-discovery.js +123 -0
- package/dist/src/text-similarity.js +24 -0
- package/dist/src/tier-maps.js +158 -0
- package/dist/src/types.js +5 -0
- package/dist/src/url-shortener.js +135 -0
- package/dist/src/verification/attestation.js +166 -0
- package/dist/src/verification/cheat-detector.js +240 -0
- package/dist/src/verification/command-runner.js +70 -0
- package/dist/src/verification/composite-score.js +157 -0
- package/dist/src/verification/cosign-attestation.js +137 -0
- package/dist/src/verification/diff-analysis.js +89 -0
- package/dist/src/verification/differential-gate.js +173 -0
- package/dist/src/verification/index.js +41 -0
- package/dist/src/verification/mutation-gate.js +242 -0
- package/dist/src/verification/property-gate.js +201 -0
- package/dist/src/verification/test-synthesizer.js +243 -0
- package/dist/src/verifier/outcome-checks.js +396 -0
- package/dist/src/verifier/transcript-checks.js +207 -0
- package/dist/src/verifier/verification-reporters.js +148 -0
- package/dist/src/verifier-engine.js +491 -0
- package/dist/src/wave-resizer.js +131 -0
- package/dist/src/wave-scheduler.js +44 -0
- package/dist/src/worktree-manager.js +368 -0
- package/dist/src/worktree-reserved-paths.js +97 -0
- package/package.json +80 -0
- package/plugin/agents/.gitkeep +0 -0
- package/plugin/agents/backend_master.agent.md +72 -0
- package/plugin/agents/devops_pro.agent.md +45 -0
- package/plugin/agents/frontend_expert.agent.md +59 -0
- package/plugin/agents/integrator_finalizer.agent.md +44 -0
- package/plugin/agents/meta_reviewer.agent.md +44 -0
- package/plugin/agents/security_auditor.agent.md +43 -0
- package/plugin/agents/tester_elite.agent.md +43 -0
- package/plugin/hooks/hooks.json +19 -0
- package/plugin/hooks/post-tool-use.json +9 -0
- package/plugin/hooks/pre-tool-use.json +9 -0
- package/plugin/plugin.json +47 -0
- package/plugin/skills/swarm-gates/SKILL.md +53 -0
- package/plugin/skills/swarm-orchestrate/SKILL.md +34 -0
- package/plugin/skills/swarm-verify/SKILL.md +44 -0
- package/templates/README.md +35 -0
- package/templates/cli-tool.json +31 -0
- package/templates/fullstack.json +52 -0
- package/templates/library.json +38 -0
- package/templates/react-app.json +45 -0
- package/templates/recipes/add-api-docs.json +35 -0
- package/templates/recipes/add-auth.json +47 -0
- package/templates/recipes/add-ci.json +39 -0
- package/templates/recipes/add-tests.json +39 -0
- package/templates/recipes/migrate-to-ts.json +42 -0
- package/templates/recipes/refactor-modularize.json +44 -0
- package/templates/recipes/security-audit.json +35 -0
- package/templates/rest-api.json +38 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.PMAgent = void 0;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const config_loader_1 = require("./config-loader");
|
|
43
|
+
const session_executor_1 = __importDefault(require("./session-executor"));
|
|
44
|
+
const logger_1 = require("./logger");
|
|
45
|
+
const logger = (0, logger_1.getLogger)('pm-agent');
|
|
46
|
+
// Approximate tokens per character (GPT-family average).
|
|
47
|
+
const CHARS_PER_TOKEN = 4;
|
|
48
|
+
/**
|
|
49
|
+
* PMAgent - Project Manager agent that reviews a generated plan before
|
|
50
|
+
* swarm execution. Validates step ordering, dependency correctness,
|
|
51
|
+
* scope coverage, and agent assignment.
|
|
52
|
+
*
|
|
53
|
+
* Usage:
|
|
54
|
+
* const pm = new PMAgent(agents);
|
|
55
|
+
* const result = pm.reviewPlan(plan);
|
|
56
|
+
*
|
|
57
|
+
* The review is purely local (no LLM call) by default. When a Copilot
|
|
58
|
+
* CLI session is available, it can optionally run an LLM-assisted review.
|
|
59
|
+
*/
|
|
60
|
+
class PMAgent {
|
|
61
|
+
availableAgents;
|
|
62
|
+
constructor(availableAgents) {
|
|
63
|
+
this.availableAgents = availableAgents;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Run a local (deterministic) review of the plan.
|
|
67
|
+
* Checks for common issues:
|
|
68
|
+
* - Circular dependencies
|
|
69
|
+
* - References to non-existent agents
|
|
70
|
+
* - Orphaned steps (no dependency path to step 1)
|
|
71
|
+
* - Missing integration/finalization step
|
|
72
|
+
* - Duplicate step numbers
|
|
73
|
+
*/
|
|
74
|
+
reviewPlan(plan) {
|
|
75
|
+
const start = Date.now();
|
|
76
|
+
const reviewNotes = [];
|
|
77
|
+
const changesApplied = [];
|
|
78
|
+
const revisedPlan = this.deepCopyPlan(plan);
|
|
79
|
+
// 1. Check for duplicate step numbers
|
|
80
|
+
const stepNumbers = revisedPlan.steps.map(s => s.stepNumber);
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
for (const num of stepNumbers) {
|
|
83
|
+
if (seen.has(num)) {
|
|
84
|
+
reviewNotes.push(`Duplicate step number: ${num}`);
|
|
85
|
+
}
|
|
86
|
+
seen.add(num);
|
|
87
|
+
}
|
|
88
|
+
// 2. Check for references to non-existent agents
|
|
89
|
+
const agentNames = new Set(this.availableAgents.map(a => a.name));
|
|
90
|
+
const normalizedAgentNames = new Set(this.availableAgents.map(a => config_loader_1.ConfigLoader.normalizeAgentName(a.name)));
|
|
91
|
+
for (const step of revisedPlan.steps) {
|
|
92
|
+
if (!agentNames.has(step.agentName) && !normalizedAgentNames.has(config_loader_1.ConfigLoader.normalizeAgentName(step.agentName))) {
|
|
93
|
+
reviewNotes.push(`Step ${step.stepNumber} references unknown agent: ${step.agentName}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// 3. Check for circular dependencies
|
|
97
|
+
const circularSteps = this.detectCircularDeps(revisedPlan.steps);
|
|
98
|
+
if (circularSteps.length > 0) {
|
|
99
|
+
reviewNotes.push(`Circular dependencies detected involving step(s): ${circularSteps.join(', ')}`);
|
|
100
|
+
}
|
|
101
|
+
// 4. Check for dependencies on non-existent steps
|
|
102
|
+
const validSteps = new Set(revisedPlan.steps.map(s => s.stepNumber));
|
|
103
|
+
for (const step of revisedPlan.steps) {
|
|
104
|
+
for (const dep of step.dependencies) {
|
|
105
|
+
if (!validSteps.has(dep)) {
|
|
106
|
+
reviewNotes.push(`Step ${step.stepNumber} depends on non-existent step ${dep}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// 5. Check for missing reviewer step (last step should be reviewer)
|
|
111
|
+
if (revisedPlan.steps.length > 1) {
|
|
112
|
+
const lastStep = revisedPlan.steps[revisedPlan.steps.length - 1];
|
|
113
|
+
const hasReviewer = lastStep.agentName === 'reviewer';
|
|
114
|
+
if (!hasReviewer && agentNames.has('reviewer')) {
|
|
115
|
+
reviewNotes.push('Plan is missing a final integration/finalization step');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// 6. Ensure metadata is up to date
|
|
119
|
+
if (!revisedPlan.metadata || revisedPlan.metadata.totalSteps !== revisedPlan.steps.length) {
|
|
120
|
+
revisedPlan.metadata = {
|
|
121
|
+
...revisedPlan.metadata,
|
|
122
|
+
totalSteps: revisedPlan.steps.length
|
|
123
|
+
};
|
|
124
|
+
changesApplied.push('Updated metadata.totalSteps');
|
|
125
|
+
}
|
|
126
|
+
const estimatedTokenCost = Math.ceil(JSON.stringify(revisedPlan).length / CHARS_PER_TOKEN);
|
|
127
|
+
return {
|
|
128
|
+
revisedPlan,
|
|
129
|
+
reviewNotes,
|
|
130
|
+
changesApplied,
|
|
131
|
+
durationMs: Date.now() - start,
|
|
132
|
+
estimatedTokenCost,
|
|
133
|
+
skipped: false
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Build a prompt for LLM-assisted plan review (for use with Copilot CLI).
|
|
138
|
+
*/
|
|
139
|
+
buildReviewPrompt(plan) {
|
|
140
|
+
const sections = [];
|
|
141
|
+
sections.push('=== PM AGENT - Plan Review ===');
|
|
142
|
+
sections.push('');
|
|
143
|
+
sections.push('You are a project manager reviewing an execution plan for a parallel');
|
|
144
|
+
sections.push('swarm of coding agents. Review the plan for:');
|
|
145
|
+
sections.push(' 1. Correct dependency ordering (no circular deps)');
|
|
146
|
+
sections.push(' 2. Appropriate agent assignment for each task');
|
|
147
|
+
sections.push(' 3. Completeness (all aspects of the goal covered)');
|
|
148
|
+
sections.push(' 4. Step granularity (not too coarse, not too fine)');
|
|
149
|
+
sections.push(' 5. Final integration step is present');
|
|
150
|
+
sections.push('');
|
|
151
|
+
sections.push('--- AVAILABLE AGENTS ---');
|
|
152
|
+
for (const agent of this.availableAgents) {
|
|
153
|
+
sections.push(` ${agent.name}: ${agent.purpose}`);
|
|
154
|
+
}
|
|
155
|
+
sections.push('');
|
|
156
|
+
sections.push('--- PLAN TO REVIEW ---');
|
|
157
|
+
sections.push(JSON.stringify(plan, null, 2));
|
|
158
|
+
sections.push('');
|
|
159
|
+
sections.push('--- INSTRUCTIONS ---');
|
|
160
|
+
sections.push('Output only a revised JSON plan object if changes are needed,');
|
|
161
|
+
sections.push('or respond with "PLAN_APPROVED" if the plan is already good.');
|
|
162
|
+
sections.push('Do not add commentary outside of a valid JSON block or PLAN_APPROVED.');
|
|
163
|
+
return sections.join('\n');
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Run an LLM-assisted review via Copilot CLI session.
|
|
167
|
+
* Falls back to local review if the session fails.
|
|
168
|
+
*/
|
|
169
|
+
async reviewPlanWithLLM(plan, sessionOptions, outputDir) {
|
|
170
|
+
const start = Date.now();
|
|
171
|
+
// Always run local review first
|
|
172
|
+
const localResult = this.reviewPlan(plan);
|
|
173
|
+
const prompt = this.buildReviewPrompt(plan);
|
|
174
|
+
const transcriptPath = path.join(outputDir, 'pm-review-transcript.md');
|
|
175
|
+
if (!fs.existsSync(outputDir)) {
|
|
176
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
const opts = {
|
|
179
|
+
...sessionOptions,
|
|
180
|
+
shareToFile: transcriptPath,
|
|
181
|
+
logPrefix: '[PM-AGENT]'
|
|
182
|
+
};
|
|
183
|
+
try {
|
|
184
|
+
const executor = new session_executor_1.default();
|
|
185
|
+
const result = await executor.executeSession(prompt, opts);
|
|
186
|
+
if (!result.success) {
|
|
187
|
+
// Fall back to local-only review
|
|
188
|
+
return {
|
|
189
|
+
...localResult,
|
|
190
|
+
reviewNotes: [...localResult.reviewNotes, 'LLM review failed, using local review only'],
|
|
191
|
+
durationMs: Date.now() - start
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// Try to parse revised plan from output
|
|
195
|
+
const revised = this.parseRevisedPlan(result.output, plan);
|
|
196
|
+
if (revised) {
|
|
197
|
+
return {
|
|
198
|
+
revisedPlan: revised,
|
|
199
|
+
reviewNotes: [...localResult.reviewNotes, 'LLM review applied changes'],
|
|
200
|
+
changesApplied: [...localResult.changesApplied, 'LLM-revised plan'],
|
|
201
|
+
durationMs: Date.now() - start,
|
|
202
|
+
estimatedTokenCost: Math.ceil((prompt.length + (result.output?.length || 0)) / CHARS_PER_TOKEN),
|
|
203
|
+
skipped: false
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// PLAN_APPROVED or unparseable output - use local result
|
|
207
|
+
return {
|
|
208
|
+
...localResult,
|
|
209
|
+
reviewNotes: [...localResult.reviewNotes, 'LLM approved plan without changes'],
|
|
210
|
+
durationMs: Date.now() - start
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
215
|
+
logger.warn(`[pm-agent] LLM review failed, using local review only: ${msg}`);
|
|
216
|
+
return {
|
|
217
|
+
...localResult,
|
|
218
|
+
reviewNotes: [...localResult.reviewNotes, 'LLM review unavailable, using local review only'],
|
|
219
|
+
durationMs: Date.now() - start
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Detect circular dependencies in plan steps.
|
|
225
|
+
* Returns step numbers involved in cycles.
|
|
226
|
+
*/
|
|
227
|
+
detectCircularDeps(steps) {
|
|
228
|
+
const visited = new Set();
|
|
229
|
+
const inStack = new Set();
|
|
230
|
+
const cycleSteps = [];
|
|
231
|
+
const stepMap = new Map(steps.map(s => [s.stepNumber, s]));
|
|
232
|
+
const dfs = (stepNum) => {
|
|
233
|
+
if (inStack.has(stepNum)) {
|
|
234
|
+
cycleSteps.push(stepNum);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
if (visited.has(stepNum))
|
|
238
|
+
return false;
|
|
239
|
+
visited.add(stepNum);
|
|
240
|
+
inStack.add(stepNum);
|
|
241
|
+
const step = stepMap.get(stepNum);
|
|
242
|
+
if (step) {
|
|
243
|
+
for (const dep of step.dependencies) {
|
|
244
|
+
if (dfs(dep)) {
|
|
245
|
+
cycleSteps.push(stepNum);
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
inStack.delete(stepNum);
|
|
251
|
+
return false;
|
|
252
|
+
};
|
|
253
|
+
for (const step of steps) {
|
|
254
|
+
dfs(step.stepNumber);
|
|
255
|
+
}
|
|
256
|
+
return [...new Set(cycleSteps)];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Try to parse a revised plan from LLM output.
|
|
260
|
+
* Returns null if output does not contain a valid plan JSON.
|
|
261
|
+
*/
|
|
262
|
+
parseRevisedPlan(output, originalPlan) {
|
|
263
|
+
if (!output || output.includes('PLAN_APPROVED'))
|
|
264
|
+
return null;
|
|
265
|
+
// Try to extract JSON from the output
|
|
266
|
+
const jsonMatch = output.match(/\{[\s\S]*"steps"[\s\S]*\}/);
|
|
267
|
+
if (!jsonMatch)
|
|
268
|
+
return null;
|
|
269
|
+
try {
|
|
270
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
271
|
+
if (parsed.steps && Array.isArray(parsed.steps)) {
|
|
272
|
+
return {
|
|
273
|
+
goal: parsed.goal || originalPlan.goal,
|
|
274
|
+
createdAt: originalPlan.createdAt,
|
|
275
|
+
steps: parsed.steps,
|
|
276
|
+
metadata: {
|
|
277
|
+
totalSteps: parsed.steps.length,
|
|
278
|
+
estimatedDuration: parsed.metadata?.estimatedDuration
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// LLM output is not valid JSON; expected when model returns prose instead of structured data
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Deep-copy a plan to avoid mutating the original.
|
|
290
|
+
*/
|
|
291
|
+
deepCopyPlan(plan) {
|
|
292
|
+
return JSON.parse(JSON.stringify(plan));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
exports.PMAgent = PMAgent;
|
|
296
|
+
exports.default = PMAgent;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runPostExecution = runPostExecution;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const logger_1 = require("./logger");
|
|
43
|
+
const analytics_log_1 = __importDefault(require("./analytics-log"));
|
|
44
|
+
const external_tool_manager_1 = __importDefault(require("./external-tool-manager"));
|
|
45
|
+
const deployment_manager_1 = __importDefault(require("./deployment-manager"));
|
|
46
|
+
const pr_automation_1 = __importDefault(require("./pr-automation"));
|
|
47
|
+
const adapters_1 = require("./adapters");
|
|
48
|
+
const logger = (0, logger_1.getLogger)('post-run');
|
|
49
|
+
async function runPostExecution(workingDir, runDir, context, plan, options) {
|
|
50
|
+
// Finalize metrics and save to analytics log
|
|
51
|
+
if (context.metricsCollector) {
|
|
52
|
+
const metrics = context.metricsCollector.finalize();
|
|
53
|
+
// Save metrics to run directory
|
|
54
|
+
const metricsPath = path.join(runDir, 'metrics.json');
|
|
55
|
+
fs.writeFileSync(metricsPath, JSON.stringify(metrics, null, 2), 'utf8');
|
|
56
|
+
// Append to analytics log
|
|
57
|
+
const analyticsLog = new analytics_log_1.default();
|
|
58
|
+
analyticsLog.appendRun(metrics);
|
|
59
|
+
logger.debug(`metrics ${metricsPath}`);
|
|
60
|
+
// Save cost attribution alongside metrics
|
|
61
|
+
if (context.costEstimate && context.stepCostRecords) {
|
|
62
|
+
const modelName = options?.model || (0, adapters_1.defaultModelForAdapter)(options?.cliAgent);
|
|
63
|
+
const totalEstimated = context.costEstimate.totalPremiumRequests;
|
|
64
|
+
const totalActual = context.stepCostRecords.reduce((s, r) => s + r.actualPremiumRequests, 0);
|
|
65
|
+
const attribution = {
|
|
66
|
+
totalEstimatedPremiumRequests: totalEstimated,
|
|
67
|
+
totalActualPremiumRequests: totalActual,
|
|
68
|
+
estimateAccuracy: context.costEstimator?.getAccuracy() ?? 1.0,
|
|
69
|
+
modelUsed: modelName,
|
|
70
|
+
modelMultiplier: context.costEstimate.modelMultiplier,
|
|
71
|
+
overageTriggered: context.costEstimate.overageCostUSD > 0,
|
|
72
|
+
perStep: context.stepCostRecords,
|
|
73
|
+
};
|
|
74
|
+
const costPath = path.join(runDir, 'cost-attribution.json');
|
|
75
|
+
fs.writeFileSync(costPath, JSON.stringify(attribution, null, 2), 'utf8');
|
|
76
|
+
}
|
|
77
|
+
// Persist session state for audit/resume support
|
|
78
|
+
// Use the runDir basename as session ID so it matches the directory
|
|
79
|
+
// the CLI created (context.executionId differs by a few ms)
|
|
80
|
+
const runDirId = path.basename(runDir);
|
|
81
|
+
const completedSteps = context.results.filter(r => r.status === 'completed');
|
|
82
|
+
const sessionState = {
|
|
83
|
+
sessionId: runDirId,
|
|
84
|
+
graph: {
|
|
85
|
+
goal: plan.goal,
|
|
86
|
+
steps: plan.steps.map(s => ({ stepNumber: s.stepNumber, task: s.task, agent: s.agentName }))
|
|
87
|
+
},
|
|
88
|
+
branchMap: Object.fromEntries(context.results
|
|
89
|
+
.filter(r => r.branchName)
|
|
90
|
+
.map(r => [String(r.stepNumber), r.branchName])),
|
|
91
|
+
transcripts: Object.fromEntries(context.results
|
|
92
|
+
.filter(r => r.sessionResult?.transcriptPath)
|
|
93
|
+
.map(r => [String(r.stepNumber), r.sessionResult.transcriptPath])),
|
|
94
|
+
metrics: metrics,
|
|
95
|
+
gateResults: context.finalGateResults || [],
|
|
96
|
+
status: completedSteps.length === plan.steps.length ? 'completed' : 'failed',
|
|
97
|
+
lastCompletedStep: Math.max(0, ...completedSteps.map(r => r.stepNumber))
|
|
98
|
+
};
|
|
99
|
+
// Write directly to runDir (saveSession uses cwd which differs for demos)
|
|
100
|
+
fs.writeFileSync(path.join(runDir, 'session-state.json'), JSON.stringify(sessionState, null, 2), 'utf8');
|
|
101
|
+
// Also persist via collector so audit/metrics CLI can find it from project root
|
|
102
|
+
context.metricsCollector.saveSession(runDirId, sessionState);
|
|
103
|
+
}
|
|
104
|
+
// OWASP ASI compliance report (when --owasp-report is set)
|
|
105
|
+
if (options?.owaspReport) {
|
|
106
|
+
try {
|
|
107
|
+
const { OwaspMapper } = await Promise.resolve().then(() => __importStar(require('./owasp-mapper')));
|
|
108
|
+
const { OwaspReportRenderer } = await Promise.resolve().then(() => __importStar(require('./owasp-report-renderer')));
|
|
109
|
+
const verificationResults = context.results
|
|
110
|
+
.map(r => r.verificationResult)
|
|
111
|
+
.filter((v) => v !== undefined);
|
|
112
|
+
const repaired = context.results.filter(r => (r.retryCount ?? 0) > 0 && r.status === 'completed').length;
|
|
113
|
+
const failed = context.results.filter(r => r.status === 'failed').length;
|
|
114
|
+
const completed = context.results.filter(r => r.status === 'completed').length;
|
|
115
|
+
const passed = completed - repaired;
|
|
116
|
+
const exhausted = context.results.filter(r => r.status === 'failed' && (r.retryCount ?? 0) > 0).length;
|
|
117
|
+
// Read version from package.json at project root
|
|
118
|
+
let toolVersion = '4.1.0';
|
|
119
|
+
try {
|
|
120
|
+
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
121
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
122
|
+
toolVersion = pkg.version || toolVersion;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Fall back to hardcoded version if package.json is unavailable
|
|
126
|
+
}
|
|
127
|
+
const meta = {
|
|
128
|
+
executionId: context.executionId,
|
|
129
|
+
toolVersion,
|
|
130
|
+
strictIsolation: !!options.strictIsolation,
|
|
131
|
+
adapterType: options.cliAgent || 'copilot',
|
|
132
|
+
totalSteps: plan.steps.length,
|
|
133
|
+
passedSteps: passed,
|
|
134
|
+
repairedSteps: repaired,
|
|
135
|
+
failedSteps: failed,
|
|
136
|
+
retriesExhausted: exhausted,
|
|
137
|
+
};
|
|
138
|
+
const mapper = new OwaspMapper();
|
|
139
|
+
const complianceReport = mapper.map(verificationResults, meta);
|
|
140
|
+
fs.writeFileSync(path.join(runDir, 'owasp-compliance.md'), OwaspReportRenderer.toMarkdown(complianceReport), 'utf8');
|
|
141
|
+
fs.writeFileSync(path.join(runDir, 'owasp-compliance.json'), OwaspReportRenderer.toJson(complianceReport), 'utf8');
|
|
142
|
+
logger.info(` OWASP ASI: ${complianceReport.mitigatedRisks}/${complianceReport.applicableRisks} applicable risks mitigated`);
|
|
143
|
+
}
|
|
144
|
+
catch (owaspErr) {
|
|
145
|
+
logger.warn(` OWASP report generation failed: ${owaspErr instanceof Error ? owaspErr.message : owaspErr}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Record execution in knowledge base
|
|
149
|
+
if (context.knowledgeBase) {
|
|
150
|
+
const totalPatternsDetected = context.waveAnalyses?.reduce((sum, analysis) => sum + analysis.detectedPatterns.length, 0) || 0;
|
|
151
|
+
context.knowledgeBase.recordRun(totalPatternsDetected);
|
|
152
|
+
// Record cost history for future estimation calibration
|
|
153
|
+
if (context.costEstimate && context.stepCostRecords) {
|
|
154
|
+
const modelName = options?.model || (0, adapters_1.defaultModelForAdapter)(options?.cliAgent);
|
|
155
|
+
const totalRetries = context.stepCostRecords.reduce((s, r) => s + r.retryCount, 0);
|
|
156
|
+
const totalActual = context.stepCostRecords.reduce((s, r) => s + r.actualPremiumRequests, 0);
|
|
157
|
+
const totalEstimated = context.costEstimate.totalPremiumRequests;
|
|
158
|
+
const evidence = {
|
|
159
|
+
runId: context.executionId,
|
|
160
|
+
estimated: totalEstimated,
|
|
161
|
+
actual: totalActual,
|
|
162
|
+
retries: totalRetries,
|
|
163
|
+
steps: plan.steps.length,
|
|
164
|
+
model: modelName,
|
|
165
|
+
};
|
|
166
|
+
context.knowledgeBase.addOrUpdatePattern({
|
|
167
|
+
category: 'cost_history',
|
|
168
|
+
insight: `${plan.steps.length} steps, model ${modelName}, ${totalActual} premium requests, ${totalRetries} retries`,
|
|
169
|
+
confidence: 'high',
|
|
170
|
+
evidence: [JSON.stringify(evidence)],
|
|
171
|
+
impact: 'medium',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Auto-create PR if requested
|
|
176
|
+
if (options?.autoPR) {
|
|
177
|
+
logger.info('\nš Creating PR...');
|
|
178
|
+
try {
|
|
179
|
+
const toolManager = new external_tool_manager_1.default({
|
|
180
|
+
enableExternal: options.enableExternal || false,
|
|
181
|
+
dryRun: options.dryRun || false,
|
|
182
|
+
logFile: path.join(runDir, 'external-commands.log')
|
|
183
|
+
});
|
|
184
|
+
const deploymentManager = new deployment_manager_1.default(toolManager, workingDir);
|
|
185
|
+
const prAutomation = new pr_automation_1.default(toolManager, workingDir);
|
|
186
|
+
const deployments = deploymentManager.loadDeploymentMetadata(runDir);
|
|
187
|
+
// generatePRSummary accepts a narrow PRSummaryContext (duck-typed in
|
|
188
|
+
// pr-automation.ts). `mainBranch` on the PostRunContext feeds
|
|
189
|
+
// PRSummary.baseBranch; plan fills in plan.goal.
|
|
190
|
+
const summary = prAutomation.generatePRSummary({ ...context, plan }, deployments);
|
|
191
|
+
const prResult = await prAutomation.createPR(summary);
|
|
192
|
+
if (prResult.success) {
|
|
193
|
+
logger.info(`ā
PR created: ${prResult.url}`);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
logger.warn(`ā ļø PR creation failed: ${prResult.error}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
logger.warn(`ā ļø PR automation error: ${error instanceof Error ? error.message : error}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRAutomation = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* PR Automation - auto-creates PRs with execution summaries
|
|
6
|
+
*/
|
|
7
|
+
class PRAutomation {
|
|
8
|
+
toolManager;
|
|
9
|
+
workingDir;
|
|
10
|
+
constructor(toolManager, workingDir) {
|
|
11
|
+
this.toolManager = toolManager;
|
|
12
|
+
this.workingDir = workingDir || process.cwd();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate PR summary from execution context
|
|
16
|
+
*/
|
|
17
|
+
generatePRSummary(context, deployments) {
|
|
18
|
+
const completedSteps = context.results.filter(r => r.status === 'completed').length;
|
|
19
|
+
const failedSteps = context.results.filter(r => r.status === 'failed').length;
|
|
20
|
+
const totalSteps = context.results.length;
|
|
21
|
+
let body = '## Swarm Execution Summary\n\n';
|
|
22
|
+
body += `**Execution ID:** \`${context.executionId}\`\n`;
|
|
23
|
+
body += `**Goal:** ${context.plan.goal}\n`;
|
|
24
|
+
body += `**Completed:** ${completedSteps}/${totalSteps} steps\n\n`;
|
|
25
|
+
if (failedSteps > 0) {
|
|
26
|
+
body += `ā ļø **${failedSteps} step(s) failed**\n\n`;
|
|
27
|
+
}
|
|
28
|
+
// Add deployment links
|
|
29
|
+
if (deployments.length > 0) {
|
|
30
|
+
body += '## š Preview Deployments\n\n';
|
|
31
|
+
for (const deployment of deployments) {
|
|
32
|
+
if (deployment.previewUrl) {
|
|
33
|
+
body += `- **${deployment.platform}** (Step ${deployment.stepNumber}): ${deployment.previewUrl}\n`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
body += '\n';
|
|
37
|
+
}
|
|
38
|
+
// Add step details
|
|
39
|
+
body += '## Steps Executed\n\n';
|
|
40
|
+
for (const result of context.results) {
|
|
41
|
+
const icon = result.status === 'completed' ? 'ā
' :
|
|
42
|
+
result.status === 'failed' ? 'ā' :
|
|
43
|
+
result.status === 'running' ? 'š§' : 'āø';
|
|
44
|
+
body += `${icon} **Step ${result.stepNumber}** (${result.agentName}): ${result.status}\n`;
|
|
45
|
+
if (result.verificationResult) {
|
|
46
|
+
const passed = result.verificationResult.passed ? 'ā' : 'ā';
|
|
47
|
+
body += ` - Verification: ${passed} ${result.verificationResult.passed ? 'passed' : 'failed'}\n`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
body += '\n---\n';
|
|
51
|
+
body += `*Generated by Swarm Orchestrator*\n`;
|
|
52
|
+
return {
|
|
53
|
+
title: `[Swarm] ${context.plan.goal}`,
|
|
54
|
+
body,
|
|
55
|
+
baseBranch: context.mainBranch,
|
|
56
|
+
headBranch: `swarm/${context.executionId}`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create PR via gh CLI
|
|
61
|
+
*/
|
|
62
|
+
async createPR(summary) {
|
|
63
|
+
const tools = await this.toolManager.detectAvailableTools();
|
|
64
|
+
if (!tools.gh) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: 'gh CLI not available'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Create PR
|
|
71
|
+
const execution = await this.toolManager.executeCommand('gh', [
|
|
72
|
+
'pr', 'create',
|
|
73
|
+
'--base', summary.baseBranch,
|
|
74
|
+
'--head', summary.headBranch,
|
|
75
|
+
'--title', summary.title,
|
|
76
|
+
'--body', summary.body
|
|
77
|
+
], {
|
|
78
|
+
workingDir: this.workingDir,
|
|
79
|
+
requireTool: 'gh'
|
|
80
|
+
});
|
|
81
|
+
if (execution.exitCode !== 0) {
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
error: execution.error || 'Failed to create PR'
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Extract PR URL from output
|
|
88
|
+
const url = this.extractPRUrl(execution.output || '');
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
...(url && { url })
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Extract PR URL from gh CLI output
|
|
96
|
+
*/
|
|
97
|
+
extractPRUrl(output) {
|
|
98
|
+
const match = output.match(/(https:\/\/github\.com\/[^\s]+)/);
|
|
99
|
+
return match ? match[1] : undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.PRAutomation = PRAutomation;
|
|
103
|
+
exports.default = PRAutomation;
|