opencode-swarm 7.3.5 → 7.3.6
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/dist/cli/index.js +1 -1
- package/dist/index.js +85 -72
- package/dist/mutation/engine.d.ts +13 -0
- package/dist/mutation/generator.d.ts +7 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.3.
|
|
37
|
+
version: "7.3.6",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var package_default;
|
|
|
33
33
|
var init_package = __esm(() => {
|
|
34
34
|
package_default = {
|
|
35
35
|
name: "opencode-swarm",
|
|
36
|
-
version: "7.3.
|
|
36
|
+
version: "7.3.6",
|
|
37
37
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
38
38
|
main: "dist/index.js",
|
|
39
39
|
types: "dist/index.d.ts",
|
|
@@ -88347,6 +88347,10 @@ init_state();
|
|
|
88347
88347
|
function slugify2(str) {
|
|
88348
88348
|
return str.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_");
|
|
88349
88349
|
}
|
|
88350
|
+
var GENERATE_MUTANTS_TIMEOUT_MS = 90000;
|
|
88351
|
+
var _internals = {
|
|
88352
|
+
timeoutMs: GENERATE_MUTANTS_TIMEOUT_MS
|
|
88353
|
+
};
|
|
88350
88354
|
function extractJsonArray(text) {
|
|
88351
88355
|
const trimmed = text.trim();
|
|
88352
88356
|
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
@@ -88378,23 +88382,24 @@ async function generateMutants(files, ctx) {
|
|
|
88378
88382
|
}
|
|
88379
88383
|
};
|
|
88380
88384
|
try {
|
|
88381
|
-
const
|
|
88382
|
-
|
|
88383
|
-
|
|
88384
|
-
|
|
88385
|
-
|
|
88386
|
-
|
|
88387
|
-
|
|
88388
|
-
|
|
88389
|
-
|
|
88390
|
-
|
|
88391
|
-
|
|
88392
|
-
|
|
88393
|
-
|
|
88394
|
-
|
|
88395
|
-
|
|
88396
|
-
|
|
88397
|
-
|
|
88385
|
+
const patches = await withTimeout((async () => {
|
|
88386
|
+
const createResult = await client.session.create({
|
|
88387
|
+
query: { directory }
|
|
88388
|
+
});
|
|
88389
|
+
if (!createResult.data) {
|
|
88390
|
+
console.warn(`[generateMutants] Failed to create session: ${JSON.stringify(createResult.error)}; returning empty patch set`);
|
|
88391
|
+
return [];
|
|
88392
|
+
}
|
|
88393
|
+
ephemeralSessionId = createResult.data.id;
|
|
88394
|
+
const mutationTypes = [
|
|
88395
|
+
"off-by-one",
|
|
88396
|
+
"null-substitution",
|
|
88397
|
+
"operator-swap",
|
|
88398
|
+
"guard-removal",
|
|
88399
|
+
"branch-swap",
|
|
88400
|
+
"side-effect-deletion"
|
|
88401
|
+
].join(", ");
|
|
88402
|
+
const promptText = `Generate mutation testing patches for the following files: ${files.join(", ")}
|
|
88398
88403
|
|
|
88399
88404
|
Return a JSON array where each element has:
|
|
88400
88405
|
{ id, filePath, functionName, mutationType, patch, lineNumber }
|
|
@@ -88405,53 +88410,55 @@ Return a JSON array where each element has:
|
|
|
88405
88410
|
- Generate 3-5 mutations per function
|
|
88406
88411
|
|
|
88407
88412
|
Return ONLY a valid JSON array. No markdown, no code fences, no explanation. Start your response with [ and end with ].`;
|
|
88408
|
-
|
|
88409
|
-
|
|
88410
|
-
|
|
88411
|
-
|
|
88412
|
-
|
|
88413
|
-
|
|
88413
|
+
const promptResult = await client.session.prompt({
|
|
88414
|
+
path: { id: ephemeralSessionId },
|
|
88415
|
+
body: {
|
|
88416
|
+
agent: undefined,
|
|
88417
|
+
tools: { write: false, edit: false, patch: false },
|
|
88418
|
+
parts: [{ type: "text", text: promptText }]
|
|
88419
|
+
}
|
|
88420
|
+
});
|
|
88421
|
+
if (!promptResult.data) {
|
|
88422
|
+
console.warn(`[generateMutants] LLM prompt failed: ${JSON.stringify(promptResult.error)}; returning empty patch set`);
|
|
88423
|
+
return [];
|
|
88414
88424
|
}
|
|
88415
|
-
|
|
88416
|
-
|
|
88417
|
-
console.warn(`[generateMutants] LLM prompt failed: ${JSON.stringify(promptResult.error)}; returning empty patch set`);
|
|
88418
|
-
return [];
|
|
88419
|
-
}
|
|
88420
|
-
const textParts = promptResult.data.parts.filter((p) => p.type === "text");
|
|
88421
|
-
const rawText = textParts.map((p) => p.text).join(`
|
|
88425
|
+
const textParts = promptResult.data.parts.filter((p) => p.type === "text");
|
|
88426
|
+
const rawText = textParts.map((p) => p.text).join(`
|
|
88422
88427
|
`);
|
|
88423
|
-
|
|
88424
|
-
|
|
88425
|
-
|
|
88426
|
-
|
|
88427
|
-
|
|
88428
|
-
|
|
88429
|
-
|
|
88430
|
-
|
|
88431
|
-
}
|
|
88432
|
-
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
88433
|
-
return [];
|
|
88434
|
-
}
|
|
88435
|
-
const patches = [];
|
|
88436
|
-
for (const item of parsed) {
|
|
88437
|
-
if (typeof item !== "object" || item === null || typeof item.filePath !== "string" || typeof item.functionName !== "string" || typeof item.mutationType !== "string" || typeof item.patch !== "string") {
|
|
88438
|
-
continue;
|
|
88428
|
+
let parsed;
|
|
88429
|
+
try {
|
|
88430
|
+
parsed = JSON.parse(extractJsonArray(rawText));
|
|
88431
|
+
} catch (error93) {
|
|
88432
|
+
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
88433
|
+
const hint = msg.includes("EOF") || msg.includes("Unexpected end") ? " (response appears truncated — LLM may have hit an output token limit)" : "";
|
|
88434
|
+
console.warn(`[generateMutants] Failed to parse LLM response as MutationPatch[]: ${msg}${hint}; returning empty patch set`);
|
|
88435
|
+
return [];
|
|
88439
88436
|
}
|
|
88440
|
-
|
|
88441
|
-
|
|
88442
|
-
|
|
88443
|
-
const
|
|
88444
|
-
const
|
|
88445
|
-
|
|
88446
|
-
|
|
88447
|
-
|
|
88448
|
-
|
|
88449
|
-
|
|
88450
|
-
|
|
88451
|
-
|
|
88452
|
-
|
|
88453
|
-
|
|
88454
|
-
|
|
88437
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
88438
|
+
return [];
|
|
88439
|
+
}
|
|
88440
|
+
const patches2 = [];
|
|
88441
|
+
for (const item of parsed) {
|
|
88442
|
+
if (typeof item !== "object" || item === null || typeof item.filePath !== "string" || typeof item.functionName !== "string" || typeof item.mutationType !== "string" || typeof item.patch !== "string") {
|
|
88443
|
+
continue;
|
|
88444
|
+
}
|
|
88445
|
+
const mutationType = item.mutationType;
|
|
88446
|
+
const fileSlug = slugify2(item.filePath);
|
|
88447
|
+
const fnSlug = slugify2(item.functionName);
|
|
88448
|
+
const typeSlug = slugify2(mutationType);
|
|
88449
|
+
const idStr = typeof item.id === "string" ? item.id : "";
|
|
88450
|
+
const id = idStr.startsWith("mut-") ? idStr : `mut-${fileSlug}-${fnSlug}-${typeSlug}-${String(patches2.length + 1).padStart(3, "0")}`;
|
|
88451
|
+
patches2.push({
|
|
88452
|
+
id,
|
|
88453
|
+
filePath: item.filePath,
|
|
88454
|
+
functionName: item.functionName,
|
|
88455
|
+
mutationType,
|
|
88456
|
+
patch: item.patch,
|
|
88457
|
+
lineNumber: typeof item.lineNumber === "number" ? item.lineNumber : undefined
|
|
88458
|
+
});
|
|
88459
|
+
}
|
|
88460
|
+
return patches2;
|
|
88461
|
+
})(), _internals.timeoutMs, new Error("generateMutants: LLM call timed out"));
|
|
88455
88462
|
return patches;
|
|
88456
88463
|
} catch (error93) {
|
|
88457
88464
|
console.warn(`[generateMutants] LLM call failed: ${error93 instanceof Error ? error93.message : String(error93)}; returning empty patch set`);
|
|
@@ -88766,6 +88773,12 @@ async function batchCheckEquivalence(patches, llmJudge) {
|
|
|
88766
88773
|
var MUTATION_TIMEOUT_MS = 30000;
|
|
88767
88774
|
var TOTAL_BUDGET_MS = 300000;
|
|
88768
88775
|
var GIT_APPLY_TIMEOUT_MS = 5000;
|
|
88776
|
+
function buildGitApplyArgs(patchFile) {
|
|
88777
|
+
return ["apply", "--ignore-whitespace", "--", patchFile];
|
|
88778
|
+
}
|
|
88779
|
+
function buildGitRevertArgs(patchFile) {
|
|
88780
|
+
return ["apply", "-R", "--ignore-whitespace", "--", patchFile];
|
|
88781
|
+
}
|
|
88769
88782
|
async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
88770
88783
|
const startTime = Date.now();
|
|
88771
88784
|
let outcome = "survived";
|
|
@@ -88792,7 +88805,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
88792
88805
|
};
|
|
88793
88806
|
}
|
|
88794
88807
|
try {
|
|
88795
|
-
const applyResult = spawnSync3("git",
|
|
88808
|
+
const applyResult = spawnSync3("git", buildGitApplyArgs(patchFile), {
|
|
88796
88809
|
cwd: workingDir,
|
|
88797
88810
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
88798
88811
|
stdio: "pipe"
|
|
@@ -88854,7 +88867,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
88854
88867
|
} finally {
|
|
88855
88868
|
if (patchFile) {
|
|
88856
88869
|
try {
|
|
88857
|
-
const revertResult = spawnSync3("git",
|
|
88870
|
+
const revertResult = spawnSync3("git", buildGitRevertArgs(patchFile), {
|
|
88858
88871
|
cwd: workingDir,
|
|
88859
88872
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
88860
88873
|
stdio: "pipe"
|
|
@@ -89681,7 +89694,7 @@ import * as path105 from "node:path";
|
|
|
89681
89694
|
init_bun_compat();
|
|
89682
89695
|
import * as fs84 from "node:fs";
|
|
89683
89696
|
import * as path104 from "node:path";
|
|
89684
|
-
var
|
|
89697
|
+
var _internals2 = { bunSpawn };
|
|
89685
89698
|
var _swarmGitExcludedChecked = false;
|
|
89686
89699
|
function fileCoversSwarm(content) {
|
|
89687
89700
|
for (const rawLine of content.split(`
|
|
@@ -89708,7 +89721,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89708
89721
|
_swarmGitExcludedChecked = true;
|
|
89709
89722
|
const { quiet = false } = options;
|
|
89710
89723
|
try {
|
|
89711
|
-
const gitRootProc =
|
|
89724
|
+
const gitRootProc = _internals2.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
|
|
89712
89725
|
let gitRootExitCode;
|
|
89713
89726
|
let gitRootOutput;
|
|
89714
89727
|
try {
|
|
@@ -89726,7 +89739,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89726
89739
|
const gitRoot = gitRootOutput.trim();
|
|
89727
89740
|
if (!gitRoot)
|
|
89728
89741
|
return;
|
|
89729
|
-
const excludePathProc =
|
|
89742
|
+
const excludePathProc = _internals2.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
|
|
89730
89743
|
let excludePathExitCode;
|
|
89731
89744
|
let excludePathRaw;
|
|
89732
89745
|
try {
|
|
@@ -89745,7 +89758,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89745
89758
|
if (!excludeRelPath)
|
|
89746
89759
|
return;
|
|
89747
89760
|
const excludePath = path104.isAbsolute(excludeRelPath) ? excludeRelPath : path104.join(directory, excludeRelPath);
|
|
89748
|
-
const checkIgnoreProc =
|
|
89761
|
+
const checkIgnoreProc = _internals2.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
|
|
89749
89762
|
let checkIgnoreExitCode;
|
|
89750
89763
|
try {
|
|
89751
89764
|
checkIgnoreExitCode = await checkIgnoreProc.exited;
|
|
@@ -89772,7 +89785,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89772
89785
|
}
|
|
89773
89786
|
} catch {}
|
|
89774
89787
|
}
|
|
89775
|
-
const trackedProc =
|
|
89788
|
+
const trackedProc = _internals2.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
|
|
89776
89789
|
let trackedExitCode;
|
|
89777
89790
|
let trackedOutput;
|
|
89778
89791
|
try {
|
|
@@ -89797,7 +89810,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89797
89810
|
}
|
|
89798
89811
|
|
|
89799
89812
|
// src/hooks/diff-scope.ts
|
|
89800
|
-
var
|
|
89813
|
+
var _internals3 = { bunSpawn };
|
|
89801
89814
|
function getDeclaredScope(taskId, directory) {
|
|
89802
89815
|
try {
|
|
89803
89816
|
const planPath = path105.join(directory, ".swarm", "plan.json");
|
|
@@ -89832,7 +89845,7 @@ var GIT_DIFF_SPAWN_OPTIONS = {
|
|
|
89832
89845
|
};
|
|
89833
89846
|
async function getChangedFiles(directory) {
|
|
89834
89847
|
try {
|
|
89835
|
-
const proc =
|
|
89848
|
+
const proc = _internals3.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
|
|
89836
89849
|
cwd: directory,
|
|
89837
89850
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
89838
89851
|
});
|
|
@@ -89849,7 +89862,7 @@ async function getChangedFiles(directory) {
|
|
|
89849
89862
|
return stdout.trim().split(`
|
|
89850
89863
|
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
89851
89864
|
}
|
|
89852
|
-
const proc2 =
|
|
89865
|
+
const proc2 = _internals3.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
|
|
89853
89866
|
cwd: directory,
|
|
89854
89867
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
89855
89868
|
});
|
|
@@ -42,6 +42,19 @@ export interface MutationReport {
|
|
|
42
42
|
timestamp: string;
|
|
43
43
|
}
|
|
44
44
|
export declare const MAX_MUTATIONS_PER_FUNCTION = 10;
|
|
45
|
+
/**
|
|
46
|
+
* Build the argument array for `git apply` with cross-platform whitespace
|
|
47
|
+
* tolerance. `--ignore-whitespace` makes git ignore CRLF/LF differences in
|
|
48
|
+
* context lines, which is required on Windows where `core.autocrlf=true`
|
|
49
|
+
* causes checked-out files to use CRLF while LLM-generated patches use LF.
|
|
50
|
+
* The flag is a no-op on macOS and Linux (files already use LF).
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildGitApplyArgs(patchFile: string): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Build the argument array for `git apply -R` (revert) with the same
|
|
55
|
+
* cross-platform whitespace tolerance as `buildGitApplyArgs`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildGitRevertArgs(patchFile: string): string[];
|
|
45
58
|
export declare function executeMutation(patch: MutationPatch, testCommand: string[], _testFiles: string[], workingDir: string): Promise<MutationResult>;
|
|
46
59
|
export declare function computeReport(results: MutationResult[], durationMs: number, budgetMs?: number): MutationReport;
|
|
47
60
|
export declare function executeMutationSuite(patches: MutationPatch[], testCommand: string[], testFiles: string[], workingDir: string, budgetMs?: number, onProgress?: (completed: number, total: number, result: MutationResult) => void, sourceFiles?: Map<string, string>): Promise<MutationReport>;
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ToolContext } from '@opencode-ai/plugin';
|
|
8
8
|
import type { MutationPatch } from './engine.js';
|
|
9
|
+
/**
|
|
10
|
+
* Dependency-injection seam. Tests may override `timeoutMs` to a short value
|
|
11
|
+
* to exercise the timeout path without waiting 90 seconds.
|
|
12
|
+
*/
|
|
13
|
+
export declare const _internals: {
|
|
14
|
+
timeoutMs: number;
|
|
15
|
+
};
|
|
9
16
|
/**
|
|
10
17
|
* Generate mutation testing patches for the given source files using an LLM.
|
|
11
18
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.6",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|