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.
Files changed (174) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +199 -0
  3. package/agents/reviewer.agent.md +50 -0
  4. package/agents/worker.agent.md +41 -0
  5. package/config/default-agents.yaml +56 -0
  6. package/config/pm-agent.yaml +36 -0
  7. package/config/quality-gates.yaml +88 -0
  8. package/config/repair-agent.yaml +34 -0
  9. package/config/semgrep-rules/complexity-mismatch.yaml +6 -0
  10. package/config/semgrep-rules/exception-swallowing.yaml +13 -0
  11. package/config/semgrep-rules/hardcoded-answer.yaml +6 -0
  12. package/config/semgrep-rules/mock-mutation.yaml +6 -0
  13. package/config/semgrep-rules/test-modification.yaml +6 -0
  14. package/config/user-agents.yaml +23 -0
  15. package/dist/src/adapters/adapter-factory.js +29 -0
  16. package/dist/src/adapters/agent-adapter.js +32 -0
  17. package/dist/src/adapters/claude-code-adapter.js +126 -0
  18. package/dist/src/adapters/claude-code-teams.js +119 -0
  19. package/dist/src/adapters/codex-adapter.js +118 -0
  20. package/dist/src/adapters/copilot-adapter.js +224 -0
  21. package/dist/src/adapters/fatal-error-classifier.js +88 -0
  22. package/dist/src/adapters/index.js +44 -0
  23. package/dist/src/adapters/persistent-session.js +204 -0
  24. package/dist/src/adapters/process-supervisor.js +199 -0
  25. package/dist/src/agents-exporter.js +398 -0
  26. package/dist/src/analytics-log.js +161 -0
  27. package/dist/src/baseline-scanner.js +65 -0
  28. package/dist/src/bootstrap-evidence.js +88 -0
  29. package/dist/src/bootstrap-orchestrator.js +205 -0
  30. package/dist/src/bootstrap-types.js +5 -0
  31. package/dist/src/branch-merger.js +427 -0
  32. package/dist/src/cli/attest-handlers.js +44 -0
  33. package/dist/src/cli/cost-prompt.js +51 -0
  34. package/dist/src/cli/demo-handlers.js +274 -0
  35. package/dist/src/cli/flags.js +152 -0
  36. package/dist/src/cli/index.js +57 -0
  37. package/dist/src/cli/live-status.js +186 -0
  38. package/dist/src/cli/misc-handlers.js +242 -0
  39. package/dist/src/cli/plan-handlers.js +350 -0
  40. package/dist/src/cli/share-handlers.js +163 -0
  41. package/dist/src/cli/status-handlers.js +457 -0
  42. package/dist/src/cli/swarm-handlers.js +638 -0
  43. package/dist/src/cli/usage.js +72 -0
  44. package/dist/src/cli.js +217 -0
  45. package/dist/src/commit-pattern-detector.js +143 -0
  46. package/dist/src/commit-quality-analyzer.js +38 -0
  47. package/dist/src/config-loader.js +339 -0
  48. package/dist/src/context-broker.js +358 -0
  49. package/dist/src/copilot-cli-wrapper.js +258 -0
  50. package/dist/src/cost-estimator.js +241 -0
  51. package/dist/src/defaults.js +18 -0
  52. package/dist/src/demo-mode.js +180 -0
  53. package/dist/src/deployment-handler.js +78 -0
  54. package/dist/src/deployment-manager.js +228 -0
  55. package/dist/src/execution-queue.js +196 -0
  56. package/dist/src/external-tool-manager.js +184 -0
  57. package/dist/src/gate-prompt-builder.js +117 -0
  58. package/dist/src/gate-remediation.js +173 -0
  59. package/dist/src/github-issues-ingester.js +152 -0
  60. package/dist/src/github-mcp-integrator.js +164 -0
  61. package/dist/src/hook-generator.js +229 -0
  62. package/dist/src/index.js +48 -0
  63. package/dist/src/knowledge-base.js +300 -0
  64. package/dist/src/logger.js +111 -0
  65. package/dist/src/meta-analyzer.js +208 -0
  66. package/dist/src/metrics-collector.js +209 -0
  67. package/dist/src/metrics-types.js +5 -0
  68. package/dist/src/multi-repo-coordinator.js +179 -0
  69. package/dist/src/orchestrator/async-meta-analysis.js +84 -0
  70. package/dist/src/orchestrator/fatal-run-error.js +43 -0
  71. package/dist/src/orchestrator/final-gates-remediation.js +258 -0
  72. package/dist/src/orchestrator/git-state-utils.js +119 -0
  73. package/dist/src/orchestrator/pause-controller.js +55 -0
  74. package/dist/src/orchestrator/replan-runner.js +258 -0
  75. package/dist/src/orchestrator/step-executor.js +391 -0
  76. package/dist/src/orchestrator/wave-scheduler-loop.js +204 -0
  77. package/dist/src/owasp-mapper.js +153 -0
  78. package/dist/src/owasp-report-renderer.js +39 -0
  79. package/dist/src/plan-files.js +142 -0
  80. package/dist/src/plan-generator.js +1207 -0
  81. package/dist/src/pm-agent.js +296 -0
  82. package/dist/src/post-run-reporter.js +203 -0
  83. package/dist/src/pr-automation.js +103 -0
  84. package/dist/src/pr-manager.js +318 -0
  85. package/dist/src/presenter/index.js +209 -0
  86. package/dist/src/prompt-builder.js +173 -0
  87. package/dist/src/quality-gates/config-loader.js +196 -0
  88. package/dist/src/quality-gates/default-config.js +243 -0
  89. package/dist/src/quality-gates/file-utils.js +152 -0
  90. package/dist/src/quality-gates/gate-runner.js +171 -0
  91. package/dist/src/quality-gates/gates/accessibility.js +355 -0
  92. package/dist/src/quality-gates/gates/duplicate-blocks.js +144 -0
  93. package/dist/src/quality-gates/gates/hardcoded-config.js +52 -0
  94. package/dist/src/quality-gates/gates/readme-claims.js +62 -0
  95. package/dist/src/quality-gates/gates/runtime-checks.js +265 -0
  96. package/dist/src/quality-gates/gates/scaffold-defaults.js +108 -0
  97. package/dist/src/quality-gates/gates/test-coverage.js +240 -0
  98. package/dist/src/quality-gates/gates/test-file-protection.js +85 -0
  99. package/dist/src/quality-gates/gates/test-isolation.js +74 -0
  100. package/dist/src/quality-gates/index.js +17 -0
  101. package/dist/src/quality-gates/registry.js +205 -0
  102. package/dist/src/quality-gates/types.js +2 -0
  103. package/dist/src/quick-fix-mode.js +302 -0
  104. package/dist/src/recipe-loader.js +122 -0
  105. package/dist/src/repair-agent.js +396 -0
  106. package/dist/src/repo-analyzer.js +342 -0
  107. package/dist/src/report-generator.js +146 -0
  108. package/dist/src/report-renderer.js +90 -0
  109. package/dist/src/requirement-filter.js +52 -0
  110. package/dist/src/sarif-formatter.js +140 -0
  111. package/dist/src/scheduling/dependency-analyzer.js +178 -0
  112. package/dist/src/scheduling/work-stealing-queue.js +123 -0
  113. package/dist/src/secret-redactor.js +90 -0
  114. package/dist/src/session-executor.js +587 -0
  115. package/dist/src/session-manager.js +221 -0
  116. package/dist/src/share/transcript-verification.js +128 -0
  117. package/dist/src/share-parser.js +477 -0
  118. package/dist/src/spinner.js +146 -0
  119. package/dist/src/step-runner.js +365 -0
  120. package/dist/src/swarm-orchestrator.js +686 -0
  121. package/dist/src/task-classifier.js +134 -0
  122. package/dist/src/test-command-discovery.js +123 -0
  123. package/dist/src/text-similarity.js +24 -0
  124. package/dist/src/tier-maps.js +158 -0
  125. package/dist/src/types.js +5 -0
  126. package/dist/src/url-shortener.js +135 -0
  127. package/dist/src/verification/attestation.js +166 -0
  128. package/dist/src/verification/cheat-detector.js +240 -0
  129. package/dist/src/verification/command-runner.js +70 -0
  130. package/dist/src/verification/composite-score.js +157 -0
  131. package/dist/src/verification/cosign-attestation.js +137 -0
  132. package/dist/src/verification/diff-analysis.js +89 -0
  133. package/dist/src/verification/differential-gate.js +173 -0
  134. package/dist/src/verification/index.js +41 -0
  135. package/dist/src/verification/mutation-gate.js +242 -0
  136. package/dist/src/verification/property-gate.js +201 -0
  137. package/dist/src/verification/test-synthesizer.js +243 -0
  138. package/dist/src/verifier/outcome-checks.js +396 -0
  139. package/dist/src/verifier/transcript-checks.js +207 -0
  140. package/dist/src/verifier/verification-reporters.js +148 -0
  141. package/dist/src/verifier-engine.js +491 -0
  142. package/dist/src/wave-resizer.js +131 -0
  143. package/dist/src/wave-scheduler.js +44 -0
  144. package/dist/src/worktree-manager.js +368 -0
  145. package/dist/src/worktree-reserved-paths.js +97 -0
  146. package/package.json +80 -0
  147. package/plugin/agents/.gitkeep +0 -0
  148. package/plugin/agents/backend_master.agent.md +72 -0
  149. package/plugin/agents/devops_pro.agent.md +45 -0
  150. package/plugin/agents/frontend_expert.agent.md +59 -0
  151. package/plugin/agents/integrator_finalizer.agent.md +44 -0
  152. package/plugin/agents/meta_reviewer.agent.md +44 -0
  153. package/plugin/agents/security_auditor.agent.md +43 -0
  154. package/plugin/agents/tester_elite.agent.md +43 -0
  155. package/plugin/hooks/hooks.json +19 -0
  156. package/plugin/hooks/post-tool-use.json +9 -0
  157. package/plugin/hooks/pre-tool-use.json +9 -0
  158. package/plugin/plugin.json +47 -0
  159. package/plugin/skills/swarm-gates/SKILL.md +53 -0
  160. package/plugin/skills/swarm-orchestrate/SKILL.md +34 -0
  161. package/plugin/skills/swarm-verify/SKILL.md +44 -0
  162. package/templates/README.md +35 -0
  163. package/templates/cli-tool.json +31 -0
  164. package/templates/fullstack.json +52 -0
  165. package/templates/library.json +38 -0
  166. package/templates/react-app.json +45 -0
  167. package/templates/recipes/add-api-docs.json +35 -0
  168. package/templates/recipes/add-auth.json +47 -0
  169. package/templates/recipes/add-ci.json +39 -0
  170. package/templates/recipes/add-tests.json +39 -0
  171. package/templates/recipes/migrate-to-ts.json +42 -0
  172. package/templates/recipes/refactor-modularize.json +44 -0
  173. package/templates/recipes/security-audit.json +35 -0
  174. package/templates/rest-api.json +38 -0
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signWithCosignKey = exports.signWithCosign = void 0;
4
+ exports.createAttestationEnvelope = createAttestationEnvelope;
5
+ exports.unsignedTestSigner = unsignedTestSigner;
6
+ exports.generateSignedAttestation = generateSignedAttestation;
7
+ exports.attachAttestationNote = attachAttestationNote;
8
+ exports.readAttestationNote = readAttestationNote;
9
+ exports.verifyAttestation = verifyAttestation;
10
+ const crypto_1 = require("crypto");
11
+ const child_process_1 = require("child_process");
12
+ const cosign_attestation_1 = require("./cosign-attestation");
13
+ var cosign_attestation_2 = require("./cosign-attestation");
14
+ Object.defineProperty(exports, "signWithCosign", { enumerable: true, get: function () { return cosign_attestation_2.signWithCosign; } });
15
+ Object.defineProperty(exports, "signWithCosignKey", { enumerable: true, get: function () { return cosign_attestation_2.signWithCosignKey; } });
16
+ const NOTE_REF = 'refs/notes/swarm-attestation';
17
+ const BUILD_TYPE = 'https://github.com/moonrunnerkc/swarm-orchestrator/attestation/v7';
18
+ function sha256(text) {
19
+ return (0, crypto_1.createHash)('sha256').update(text).digest('hex');
20
+ }
21
+ function git(repoPath, args) {
22
+ return (0, child_process_1.execFileSync)('git', args, {
23
+ cwd: repoPath,
24
+ encoding: 'utf8',
25
+ stdio: ['ignore', 'pipe', 'pipe'],
26
+ }).trim();
27
+ }
28
+ function isRecord(value) {
29
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
30
+ }
31
+ function parseSignedAttestation(raw) {
32
+ const parsed = JSON.parse(raw);
33
+ if (!isRecord(parsed) || !isRecord(parsed.envelope) || !isRecord(parsed.signature)) {
34
+ throw new Error('attestation note is not a signed attestation object');
35
+ }
36
+ return parsed;
37
+ }
38
+ /**
39
+ * Build an in-toto SLSA v1.0 provenance statement for an agent-authored commit.
40
+ *
41
+ * @param input - Attestation fields collected from the verification run.
42
+ * @returns Unsigned in-toto statement.
43
+ */
44
+ function createAttestationEnvelope(input) {
45
+ const timestamp = input.timestamp ?? new Date().toISOString();
46
+ return {
47
+ _type: 'https://in-toto.io/Statement/v1',
48
+ subject: [{
49
+ name: 'git-commit',
50
+ digest: { sha1: input.commit },
51
+ }],
52
+ predicateType: 'https://slsa.dev/provenance/v1',
53
+ predicate: {
54
+ buildType: BUILD_TYPE,
55
+ invocation: {
56
+ parameters: {
57
+ goalHash: sha256(input.goalText),
58
+ planHash: input.planHash,
59
+ },
60
+ },
61
+ buildConfig: input.agent,
62
+ metadata: {
63
+ transcriptHash: sha256(input.transcript),
64
+ layerResults: input.layerResults,
65
+ compositeScore: input.compositeScore,
66
+ timestamp,
67
+ },
68
+ },
69
+ };
70
+ }
71
+ /**
72
+ * Deterministic non-production signer for local tests.
73
+ *
74
+ * @param envelope - In-toto statement to mark as test-signed.
75
+ * @returns Test signature marker.
76
+ */
77
+ async function unsignedTestSigner(envelope) {
78
+ return {
79
+ kind: 'unsigned-test',
80
+ signature: sha256(JSON.stringify(envelope)),
81
+ };
82
+ }
83
+ /**
84
+ * Generate and sign an attestation.
85
+ *
86
+ * @param input - Attestation input and optional signer override.
87
+ * @returns Signed attestation object.
88
+ */
89
+ async function generateSignedAttestation(input) {
90
+ const envelope = createAttestationEnvelope(input);
91
+ const signer = input.signer ?? cosign_attestation_1.signWithCosign;
92
+ return {
93
+ envelope,
94
+ signature: await signer(envelope, input.repoPath),
95
+ };
96
+ }
97
+ /**
98
+ * Attach a signed attestation to a commit as a git note.
99
+ *
100
+ * @param repoPath - Repository root.
101
+ * @param commit - Commit hash to annotate.
102
+ * @param attestation - Signed attestation payload.
103
+ */
104
+ function attachAttestationNote(repoPath, commit, attestation) {
105
+ git(repoPath, [
106
+ 'notes',
107
+ `--ref=${NOTE_REF}`,
108
+ 'add',
109
+ '-f',
110
+ '-m',
111
+ JSON.stringify(attestation),
112
+ commit,
113
+ ]);
114
+ }
115
+ /**
116
+ * Read a swarm attestation git note for a commit.
117
+ *
118
+ * @param repoPath - Repository root.
119
+ * @param commit - Commit hash.
120
+ * @returns Parsed attestation, or undefined when no note exists.
121
+ */
122
+ function readAttestationNote(repoPath, commit) {
123
+ try {
124
+ return parseSignedAttestation(git(repoPath, ['notes', `--ref=${NOTE_REF}`, 'show', commit]));
125
+ }
126
+ catch {
127
+ return undefined;
128
+ }
129
+ }
130
+ /**
131
+ * Verify a swarm attestation note.
132
+ *
133
+ * Cosign signatures are verified with `cosign verify-blob` when present. The
134
+ * `unsigned-test` signature kind is accepted only for local deterministic tests.
135
+ *
136
+ * @param repoPath - Repository root.
137
+ * @param commit - Commit hash to verify.
138
+ * @returns Verification result and parsed attestation when found.
139
+ */
140
+ async function verifyAttestation(repoPath, commit) {
141
+ const attestation = readAttestationNote(repoPath, commit);
142
+ if (!attestation) {
143
+ return { found: false, verified: false, reason: 'no attestation found' };
144
+ }
145
+ const subject = attestation.envelope.subject[0]?.digest.sha1;
146
+ if (subject !== commit) {
147
+ return {
148
+ found: true,
149
+ verified: false,
150
+ reason: `attestation subject ${subject ?? 'missing'} does not match commit ${commit}`,
151
+ attestation,
152
+ };
153
+ }
154
+ if (attestation.signature.kind === 'unsigned-test') {
155
+ const expected = sha256(JSON.stringify(attestation.envelope));
156
+ return {
157
+ found: true,
158
+ verified: attestation.signature.signature === expected,
159
+ reason: attestation.signature.signature === expected
160
+ ? 'unsigned test attestation verified structurally'
161
+ : 'unsigned test attestation signature mismatch',
162
+ attestation,
163
+ };
164
+ }
165
+ return (0, cosign_attestation_1.verifyCosignSignature)(repoPath, attestation);
166
+ }
@@ -0,0 +1,240 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runCheatDetector = runCheatDetector;
37
+ const child_process_1 = require("child_process");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const diff_analysis_1 = require("./diff-analysis");
41
+ const command_runner_1 = require("./command-runner");
42
+ function gitDiff(input) {
43
+ if (input.diffText !== undefined)
44
+ return input.diffText;
45
+ const args = input.baseRef && input.patchRef
46
+ ? ['diff', '--unified=0', `${input.baseRef}..${input.patchRef}`]
47
+ : ['diff', '--unified=0'];
48
+ return (0, child_process_1.execFileSync)('git', args, {
49
+ cwd: input.repoPath,
50
+ encoding: 'utf8',
51
+ stdio: ['ignore', 'pipe', 'pipe'],
52
+ });
53
+ }
54
+ function findSemgrepConfig(input) {
55
+ const candidates = [
56
+ input.semgrepConfigDir,
57
+ path.join(process.cwd(), 'config', 'semgrep-rules'),
58
+ path.join(__dirname, '..', '..', '..', 'config', 'semgrep-rules'),
59
+ path.join(__dirname, '..', '..', 'config', 'semgrep-rules'),
60
+ ].filter((candidate) => Boolean(candidate));
61
+ return candidates.find(candidate => fs.existsSync(candidate));
62
+ }
63
+ function addedLineCount(files) {
64
+ return files.reduce((sum, file) => (sum + file.lines.filter(line => line.kind === 'add' && line.content.trim()).length), 0);
65
+ }
66
+ function changedImplementationFiles(files) {
67
+ return files.filter(file => !(0, diff_analysis_1.isTestFilePath)(file.newPath));
68
+ }
69
+ function changedTestFiles(files) {
70
+ return files.filter(file => (0, diff_analysis_1.isTestFilePath)(file.newPath));
71
+ }
72
+ function detectTestModification(files, allowedTestFiles) {
73
+ const allowed = new Set(allowedTestFiles ?? []);
74
+ return changedTestFiles(files)
75
+ .filter(file => !allowed.has(file.newPath))
76
+ .map(file => ({
77
+ rule: 'test-modification',
78
+ severity: 'high',
79
+ filePath: file.newPath,
80
+ explanation: 'patch modifies a test file that was not listed in the goal allowlist',
81
+ }));
82
+ }
83
+ function detectComplexityMismatch(files, goalText) {
84
+ // Only scan the goal title (first non-empty line). Long-form issue prose
85
+ // contains many commas and conjunctions that do not represent goal steps.
86
+ const title = goalText.split(/\r?\n/).find(line => line.trim() !== '') ?? '';
87
+ const multiStepSignals = (title.match(/\b(?:and|with|including|plus|also|all|multiple)\b|[,;]/gi) ?? []).length;
88
+ if (multiStepSignals < 3 || addedLineCount(files) >= 5)
89
+ return [];
90
+ return [{
91
+ rule: 'complexity-mismatch',
92
+ severity: 'low',
93
+ filePath: changedImplementationFiles(files)[0]?.newPath ?? 'unknown',
94
+ explanation: 'goal describes multiple behaviors but the patch adds fewer than five substantive lines',
95
+ }];
96
+ }
97
+ function detectMockMutation(files) {
98
+ const implementationChanged = changedImplementationFiles(files).length > 0;
99
+ if (implementationChanged)
100
+ return [];
101
+ const mockRe = /\b(mock(?:ReturnValue|ResolvedValue|Implementation)?|fixture|beforeEach|setUp|setup)\b/i;
102
+ const findings = [];
103
+ for (const file of changedTestFiles(files)) {
104
+ const line = file.lines.find(entry => (entry.kind === 'add' || entry.kind === 'remove') && mockRe.test(entry.content));
105
+ if (line) {
106
+ findings.push({
107
+ rule: 'mock-mutation',
108
+ severity: 'high',
109
+ filePath: file.newPath,
110
+ ...(line.newLine ? { line: line.newLine } : {}),
111
+ explanation: 'patch changes mock or fixture setup without changing implementation code',
112
+ });
113
+ }
114
+ }
115
+ return findings;
116
+ }
117
+ function detectExceptionSwallowing(files) {
118
+ const findings = [];
119
+ for (const file of changedImplementationFiles(files)) {
120
+ const added = file.lines.filter(line => line.kind === 'add');
121
+ for (let i = 0; i < added.length; i += 1) {
122
+ const line = added[i];
123
+ const next = added.slice(i + 1, i + 4).map(entry => entry.content.trim());
124
+ const body = next.join(' ');
125
+ if (/catch\s*(?:\([^)]*\))?\s*\{\s*\}?/.test(line.content)
126
+ && (/\bcatch[^{]*\{\s*\}\s*$/.test(line.content)
127
+ || /^(?:\}|console\.|logger\.)/.test(body))) {
128
+ findings.push({
129
+ rule: 'exception-swallowing',
130
+ severity: 'high',
131
+ filePath: file.newPath,
132
+ ...(line.newLine ? { line: line.newLine } : {}),
133
+ explanation: 'patch adds a catch handler that appears empty or log-only',
134
+ });
135
+ }
136
+ }
137
+ }
138
+ return findings;
139
+ }
140
+ function expectedLiteralsFromTests(files) {
141
+ const literals = new Set();
142
+ for (const file of changedTestFiles(files)) {
143
+ for (const line of file.lines) {
144
+ if (line.kind === 'add' && /\b(assert|expect|toEqual|strictEqual|deepStrictEqual)\b/.test(line.content)) {
145
+ for (const literal of (0, diff_analysis_1.extractLiterals)(line.content))
146
+ literals.add(literal);
147
+ }
148
+ }
149
+ }
150
+ return literals;
151
+ }
152
+ function detectHardcodedAnswers(files) {
153
+ const expected = expectedLiteralsFromTests(files);
154
+ if (expected.size === 0)
155
+ return [];
156
+ const findings = [];
157
+ for (const file of changedImplementationFiles(files)) {
158
+ for (const line of file.lines.filter(entry => entry.kind === 'add')) {
159
+ const overlap = (0, diff_analysis_1.extractLiterals)(line.content).find(literal => expected.has(literal));
160
+ if (overlap) {
161
+ findings.push({
162
+ rule: 'hardcoded-answer',
163
+ severity: 'medium',
164
+ filePath: file.newPath,
165
+ ...(line.newLine ? { line: line.newLine } : {}),
166
+ explanation: `implementation adds literal "${overlap}" that also appears in test expectations`,
167
+ });
168
+ }
169
+ }
170
+ }
171
+ return findings;
172
+ }
173
+ function scoreFindings(findings) {
174
+ const penalty = findings.reduce((sum, finding) => {
175
+ if (finding.severity === 'high')
176
+ return sum + 0.35;
177
+ if (finding.severity === 'medium')
178
+ return sum + 0.2;
179
+ return sum + 0.1;
180
+ }, 0);
181
+ return Math.max(0, Number((1 - penalty).toFixed(3)));
182
+ }
183
+ async function runSemgrep(input, files) {
184
+ if (input.runSemgrep === false)
185
+ return { status: 'not-run', findings: [] };
186
+ const configDir = findSemgrepConfig(input);
187
+ if (!configDir)
188
+ return { status: 'unavailable', findings: [] };
189
+ const fileArgs = files.map(file => `'${file.newPath.replace(/'/g, `'\\''`)}'`).join(' ');
190
+ if (!fileArgs)
191
+ return { status: 'not-run', findings: [] };
192
+ const command = `semgrep --config '${configDir.replace(/'/g, `'\\''`)}' --json ${fileArgs}`;
193
+ const result = await (0, command_runner_1.runVerificationCommand)(command, input.repoPath, 120_000);
194
+ if (result.exitCode !== 0 && !result.stdout.trim())
195
+ return { status: 'failed', findings: [] };
196
+ try {
197
+ const parsed = JSON.parse(result.stdout);
198
+ const findings = (parsed.results ?? []).flatMap((entry) => {
199
+ if (!entry || typeof entry !== 'object')
200
+ return [];
201
+ const record = entry;
202
+ const pathValue = typeof record.path === 'string' ? record.path : 'unknown';
203
+ const checkId = typeof record.check_id === 'string' ? record.check_id : 'semgrep';
204
+ return [{
205
+ rule: checkId,
206
+ severity: 'medium',
207
+ filePath: pathValue,
208
+ explanation: 'semgrep rule pack finding',
209
+ }];
210
+ });
211
+ return { status: 'passed', findings };
212
+ }
213
+ catch {
214
+ return { status: 'failed', findings: [] };
215
+ }
216
+ }
217
+ /**
218
+ * Detect common ways agent patches game verification instead of fixing code.
219
+ *
220
+ * @param input - Repo, goal text, diff source, and Semgrep settings.
221
+ * @returns Per-rule findings and advisory score.
222
+ */
223
+ async function runCheatDetector(input) {
224
+ const diff = gitDiff(input);
225
+ const files = (0, diff_analysis_1.parseUnifiedDiff)(diff);
226
+ const diffFindings = [
227
+ ...detectHardcodedAnswers(files),
228
+ ...detectExceptionSwallowing(files),
229
+ ...detectTestModification(files, input.allowedTestFiles),
230
+ ...detectComplexityMismatch(files, input.goalText),
231
+ ...detectMockMutation(files),
232
+ ];
233
+ const semgrep = await runSemgrep(input, files);
234
+ const findings = [...diffFindings, ...semgrep.findings];
235
+ return {
236
+ score: scoreFindings(findings),
237
+ findings,
238
+ semgrepStatus: semgrep.status,
239
+ };
240
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runVerificationCommand = runVerificationCommand;
4
+ const child_process_1 = require("child_process");
5
+ /**
6
+ * Run a shell command for a verification gate and capture its complete output.
7
+ *
8
+ * @param command - Shell command to run.
9
+ * @param cwd - Working directory for the command.
10
+ * @param timeoutMs - Maximum runtime before the process is killed.
11
+ * @returns Structured command output, exit code, timeout flag, and duration.
12
+ */
13
+ function runVerificationCommand(command, cwd, timeoutMs = 120_000) {
14
+ const started = Date.now();
15
+ return new Promise((resolve) => {
16
+ const proc = (0, child_process_1.spawn)(command, {
17
+ cwd,
18
+ shell: true,
19
+ env: {
20
+ ...process.env,
21
+ GIT_TERMINAL_PROMPT: '0',
22
+ },
23
+ stdio: ['ignore', 'pipe', 'pipe'],
24
+ });
25
+ let stdout = '';
26
+ let stderr = '';
27
+ let timedOut = false;
28
+ let resolved = false;
29
+ const finish = (exitCode) => {
30
+ if (resolved)
31
+ return;
32
+ resolved = true;
33
+ clearTimeout(timeout);
34
+ resolve({
35
+ command,
36
+ cwd,
37
+ exitCode,
38
+ stdout,
39
+ stderr,
40
+ durationMs: Date.now() - started,
41
+ timedOut,
42
+ });
43
+ };
44
+ const timeout = setTimeout(() => {
45
+ timedOut = true;
46
+ proc.kill('SIGTERM');
47
+ setTimeout(() => {
48
+ try {
49
+ proc.kill('SIGKILL');
50
+ }
51
+ catch {
52
+ // Process already exited.
53
+ }
54
+ }, 2_000);
55
+ }, timeoutMs);
56
+ proc.stdout?.on('data', (data) => {
57
+ stdout += data.toString();
58
+ });
59
+ proc.stderr?.on('data', (data) => {
60
+ stderr += data.toString();
61
+ });
62
+ proc.on('close', (code) => {
63
+ finish(code ?? 1);
64
+ });
65
+ proc.on('error', (err) => {
66
+ stderr += stderr ? `\n${err.message}` : err.message;
67
+ finish(1);
68
+ });
69
+ });
70
+ }
@@ -0,0 +1,157 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_COMPOSITE_CONFIG = void 0;
37
+ exports.loadCompositeScoreConfig = loadCompositeScoreConfig;
38
+ exports.computeCompositeScore = computeCompositeScore;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const yaml = __importStar(require("js-yaml"));
42
+ exports.DEFAULT_COMPOSITE_CONFIG = {
43
+ threshold: 0.7,
44
+ weights: {
45
+ cheatDetector: 0.4,
46
+ propertyGate: 0.4,
47
+ attestation: 0.2,
48
+ },
49
+ advisoryGatePenalty: 0.02,
50
+ gateWeights: {},
51
+ };
52
+ function isRecord(value) {
53
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
54
+ }
55
+ function readNumber(record, key) {
56
+ const value = record[key];
57
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
58
+ }
59
+ function clampScore(score) {
60
+ return Math.max(0, Math.min(1, score));
61
+ }
62
+ function mergeConfig(config) {
63
+ return {
64
+ ...exports.DEFAULT_COMPOSITE_CONFIG,
65
+ ...config,
66
+ weights: {
67
+ ...exports.DEFAULT_COMPOSITE_CONFIG.weights,
68
+ ...config?.weights,
69
+ },
70
+ gateWeights: {
71
+ ...exports.DEFAULT_COMPOSITE_CONFIG.gateWeights,
72
+ ...config?.gateWeights,
73
+ },
74
+ };
75
+ }
76
+ /**
77
+ * Load composite scoring configuration from `.swarm/gates.yaml`.
78
+ *
79
+ * @param projectRoot - Target repository root.
80
+ * @returns Composite score configuration.
81
+ */
82
+ function loadCompositeScoreConfig(projectRoot) {
83
+ const configPath = path.join(projectRoot, '.swarm', 'gates.yaml');
84
+ if (!fs.existsSync(configPath))
85
+ return mergeConfig();
86
+ const parsed = yaml.load(fs.readFileSync(configPath, 'utf8'));
87
+ if (!isRecord(parsed) || !isRecord(parsed.verification))
88
+ return mergeConfig();
89
+ const composite = parsed.verification.composite;
90
+ if (!isRecord(composite))
91
+ return mergeConfig();
92
+ const weights = isRecord(composite.weights) ? composite.weights : {};
93
+ const gateWeights = isRecord(composite.gateWeights) ? composite.gateWeights : {};
94
+ const cleanedGateWeights = {};
95
+ for (const [key, value] of Object.entries(gateWeights)) {
96
+ if (typeof value === 'number' && Number.isFinite(value))
97
+ cleanedGateWeights[key] = value;
98
+ }
99
+ const override = {
100
+ weights: {
101
+ cheatDetector: readNumber(weights, 'cheatDetector') ?? exports.DEFAULT_COMPOSITE_CONFIG.weights.cheatDetector,
102
+ propertyGate: readNumber(weights, 'propertyGate') ?? exports.DEFAULT_COMPOSITE_CONFIG.weights.propertyGate,
103
+ attestation: readNumber(weights, 'attestation') ?? exports.DEFAULT_COMPOSITE_CONFIG.weights.attestation,
104
+ },
105
+ gateWeights: cleanedGateWeights,
106
+ };
107
+ const threshold = readNumber(composite, 'threshold');
108
+ const advisoryGatePenalty = readNumber(composite, 'advisoryGatePenalty');
109
+ if (threshold !== undefined)
110
+ override.threshold = threshold;
111
+ if (advisoryGatePenalty !== undefined)
112
+ override.advisoryGatePenalty = advisoryGatePenalty;
113
+ return mergeConfig(override);
114
+ }
115
+ function layerScore(input, config) {
116
+ const weightSum = config.weights.cheatDetector + config.weights.propertyGate + config.weights.attestation;
117
+ if (weightSum <= 0)
118
+ return 0;
119
+ const weighted = clampScore(input.cheatDetectorScore) * config.weights.cheatDetector +
120
+ clampScore(input.propertyGateScore) * config.weights.propertyGate +
121
+ clampScore(input.attestationScore) * config.weights.attestation;
122
+ return weighted / weightSum;
123
+ }
124
+ function advisoryPenalty(gates, config) {
125
+ if (!gates || gates.length === 0)
126
+ return 0;
127
+ return gates
128
+ .filter(gate => gate.status === 'fail')
129
+ .reduce((sum, gate) => sum + (config.gateWeights[gate.id] ?? config.advisoryGatePenalty), 0);
130
+ }
131
+ function advisoryLayerTriggered(statuses) {
132
+ if (statuses === undefined)
133
+ return false;
134
+ const values = Array.isArray(statuses) ? statuses : Object.values(statuses);
135
+ return values.some(status => status === 'advisory-warn' || status === 'WARNING');
136
+ }
137
+ /**
138
+ * Compute the P1 advisory composite score.
139
+ *
140
+ * @param input - Layer 3-5 scores, advisory gates, and optional config.
141
+ * @returns Composite score and human-review decision.
142
+ */
143
+ function computeCompositeScore(input) {
144
+ const config = mergeConfig(input.config);
145
+ const weightedLayerScore = layerScore(input, config);
146
+ const penalty = advisoryPenalty(input.advisoryGateResults, config);
147
+ const score = Number(clampScore(weightedLayerScore - penalty).toFixed(3));
148
+ const advisoryTriggered = advisoryLayerTriggered(input.advisoryLayerStatuses);
149
+ return {
150
+ score,
151
+ threshold: config.threshold,
152
+ humanReviewRequired: score < config.threshold || advisoryTriggered,
153
+ advisoryLayerTriggered: advisoryTriggered,
154
+ advisoryPenalty: Number(penalty.toFixed(3)),
155
+ weightedLayerScore: Number(weightedLayerScore.toFixed(3)),
156
+ };
157
+ }