opencode-swarm 7.3.4 → 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/README.md +11 -0
- package/dist/agents/index.d.ts +28 -0
- package/dist/cli/index.js +7 -2
- package/dist/config/schema.d.ts +1 -20
- package/dist/index.js +155 -83
- package/dist/mutation/engine.d.ts +13 -0
- package/dist/mutation/generator.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -788,6 +788,17 @@ Prefixed agents (e.g., `paid_coder`, `mega_reviewer`, `local_architect`) inherit
|
|
|
788
788
|
|
|
789
789
|
In this example, `paid_coder` gets its own explicit rule, while other prefixed coders (e.g., `mega_coder`) fall back to `coder`.
|
|
790
790
|
|
|
791
|
+
#### Selecting the primary agent in multi-swarm configs (`default_agent`)
|
|
792
|
+
|
|
793
|
+
The top-level `default_agent` field controls which generated agents OpenCode treats as primary. **It is optional.** Behavior:
|
|
794
|
+
|
|
795
|
+
- **Omitted** — every architect-role agent is primary. In a multi-swarm config that means each swarm exposes its own architect (`local_architect`, `mega_architect`, `paid_architect`, `modelrelay_architect`, …) as a selectable session default. This is the v7.0.0-compatible behavior and the recommended setup.
|
|
796
|
+
- **Base role** (e.g. `"coder"`) — every generated agent whose canonical base role matches becomes primary (`local_coder`, `mega_coder`, …).
|
|
797
|
+
- **Exact generated name** (e.g. `"local_architect"`) — only that agent is primary.
|
|
798
|
+
- **Unknown / invalid value** — a one-time warning is logged and the resolver falls back to architect-role primaries (or the first generated agent if architects are disabled). The plugin never produces zero primaries when at least one agent exists.
|
|
799
|
+
|
|
800
|
+
See [`docs/configuration.md`](docs/configuration.md) for the full table.
|
|
801
|
+
|
|
791
802
|
### Runtime Enforcement
|
|
792
803
|
|
|
793
804
|
Architect direct writes are enforced at runtime via `toolBefore` hook. This tracks writes to source code paths outside `.swarm/` and protects `.swarm/plan.md` and `.swarm/plan.json` from direct modification.
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -34,6 +34,34 @@ export declare function getSwarmAgents(): Record<string, {
|
|
|
34
34
|
* Create all agent definitions with configuration applied
|
|
35
35
|
*/
|
|
36
36
|
export declare function createAgents(config?: PluginConfig): AgentDefinition[];
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the set of generated agent names that should be marked as primary
|
|
39
|
+
* for OpenCode's session-default-agent resolution.
|
|
40
|
+
*
|
|
41
|
+
* Resolution rules (see schema.ts default_agent comment for full semantics):
|
|
42
|
+
* - default_agent omitted ⇒ every architect-role agent is primary
|
|
43
|
+
* (canonical base role === "architect"). This restores v7.0.0 behavior in
|
|
44
|
+
* multi-swarm configs where there is no unprefixed `architect` agent.
|
|
45
|
+
* - default_agent exactly matches a generated agent name ⇒ only that agent.
|
|
46
|
+
* Exact match wins over base-role match — `local_architect` resolves to
|
|
47
|
+
* just `local_architect`, never the entire architect role.
|
|
48
|
+
* - default_agent is a base role in ALL_AGENT_NAMES ⇒ every generated agent
|
|
49
|
+
* whose canonical base role matches that role.
|
|
50
|
+
* - default_agent is invalid (matches nothing) ⇒ fall back to architect-role
|
|
51
|
+
* primaries; if no architect roles exist (architects disabled), fall back
|
|
52
|
+
* to the first generated agent. Always warns. Never returns empty when
|
|
53
|
+
* `agentNames` is non-empty.
|
|
54
|
+
*
|
|
55
|
+
* Important matching detail: a value like "not_an_architect" is NOT treated
|
|
56
|
+
* as a base-role request even though stripKnownSwarmPrefix() returns
|
|
57
|
+
* "architect" for it. Base-role matching only fires when the user-supplied
|
|
58
|
+
* value is itself one of ALL_AGENT_NAMES.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolvePrimaryAgentNames(agentNames: string[], defaultAgent?: string): {
|
|
61
|
+
primaryNames: Set<string>;
|
|
62
|
+
reason: 'implicit-architects' | 'exact' | 'base-role' | 'fallback-architects' | 'fallback-first';
|
|
63
|
+
warning?: string;
|
|
64
|
+
};
|
|
37
65
|
/**
|
|
38
66
|
* Get agent configurations formatted for the OpenCode SDK.
|
|
39
67
|
*/
|
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",
|
|
@@ -16915,7 +16915,12 @@ var init_schema = __esm(() => {
|
|
|
16915
16915
|
});
|
|
16916
16916
|
PluginConfigSchema = exports_external.object({
|
|
16917
16917
|
agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
|
|
16918
|
-
default_agent: exports_external.
|
|
16918
|
+
default_agent: exports_external.string().optional().transform((v) => {
|
|
16919
|
+
if (v === undefined)
|
|
16920
|
+
return;
|
|
16921
|
+
const trimmed = v.trim();
|
|
16922
|
+
return trimmed === "" ? undefined : trimmed;
|
|
16923
|
+
}),
|
|
16919
16924
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
16920
16925
|
max_iterations: exports_external.number().min(1).max(10).default(5),
|
|
16921
16926
|
pipeline: PipelineConfigSchema.optional(),
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -643,26 +643,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
643
643
|
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
644
644
|
fallback_models: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
645
645
|
}, z.core.$strip>>>;
|
|
646
|
-
default_agent: z.
|
|
647
|
-
architect: "architect";
|
|
648
|
-
sme: "sme";
|
|
649
|
-
docs: "docs";
|
|
650
|
-
designer: "designer";
|
|
651
|
-
critic_sounding_board: "critic_sounding_board";
|
|
652
|
-
critic_drift_verifier: "critic_drift_verifier";
|
|
653
|
-
critic_hallucination_verifier: "critic_hallucination_verifier";
|
|
654
|
-
curator_init: "curator_init";
|
|
655
|
-
curator_phase: "curator_phase";
|
|
656
|
-
council_generalist: "council_generalist";
|
|
657
|
-
council_skeptic: "council_skeptic";
|
|
658
|
-
council_domain_expert: "council_domain_expert";
|
|
659
|
-
reviewer: "reviewer";
|
|
660
|
-
critic: "critic";
|
|
661
|
-
critic_oversight: "critic_oversight";
|
|
662
|
-
explorer: "explorer";
|
|
663
|
-
coder: "coder";
|
|
664
|
-
test_engineer: "test_engineer";
|
|
665
|
-
}>>>;
|
|
646
|
+
default_agent: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string | undefined, string | undefined>>;
|
|
666
647
|
swarms: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
667
648
|
name: z.ZodOptional<z.ZodString>;
|
|
668
649
|
agents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
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",
|
|
@@ -15352,7 +15352,12 @@ var init_schema = __esm(() => {
|
|
|
15352
15352
|
});
|
|
15353
15353
|
PluginConfigSchema = exports_external.object({
|
|
15354
15354
|
agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
|
|
15355
|
-
default_agent: exports_external.
|
|
15355
|
+
default_agent: exports_external.string().optional().transform((v) => {
|
|
15356
|
+
if (v === undefined)
|
|
15357
|
+
return;
|
|
15358
|
+
const trimmed = v.trim();
|
|
15359
|
+
return trimmed === "" ? undefined : trimmed;
|
|
15360
|
+
}),
|
|
15356
15361
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
15357
15362
|
max_iterations: exports_external.number().min(1).max(10).default(5),
|
|
15358
15363
|
pipeline: PipelineConfigSchema.optional(),
|
|
@@ -59986,6 +59991,52 @@ function createAgents(config3) {
|
|
|
59986
59991
|
}
|
|
59987
59992
|
return allAgents;
|
|
59988
59993
|
}
|
|
59994
|
+
function resolvePrimaryAgentNames(agentNames, defaultAgent) {
|
|
59995
|
+
const collectArchitectRole = () => agentNames.filter((n) => stripKnownSwarmPrefix(n) === "architect");
|
|
59996
|
+
const trimmed = typeof defaultAgent === "string" ? defaultAgent.trim() : undefined;
|
|
59997
|
+
const value = trimmed === "" ? undefined : trimmed;
|
|
59998
|
+
if (agentNames.length === 0) {
|
|
59999
|
+
return { primaryNames: new Set, reason: "implicit-architects" };
|
|
60000
|
+
}
|
|
60001
|
+
if (value === undefined) {
|
|
60002
|
+
const architects2 = collectArchitectRole();
|
|
60003
|
+
if (architects2.length > 0) {
|
|
60004
|
+
return {
|
|
60005
|
+
primaryNames: new Set(architects2),
|
|
60006
|
+
reason: "implicit-architects"
|
|
60007
|
+
};
|
|
60008
|
+
}
|
|
60009
|
+
const first2 = agentNames[0];
|
|
60010
|
+
return {
|
|
60011
|
+
primaryNames: new Set([first2]),
|
|
60012
|
+
reason: "fallback-first",
|
|
60013
|
+
warning: `[swarm] No architect-role agents are registered and default_agent is unset; falling back to '${first2}' as primary. Re-enable an architect agent or set default_agent to silence this warning.`
|
|
60014
|
+
};
|
|
60015
|
+
}
|
|
60016
|
+
if (ALL_AGENT_NAMES.includes(value)) {
|
|
60017
|
+
const matching = agentNames.filter((n) => stripKnownSwarmPrefix(n) === value);
|
|
60018
|
+
if (matching.length > 0) {
|
|
60019
|
+
return { primaryNames: new Set(matching), reason: "base-role" };
|
|
60020
|
+
}
|
|
60021
|
+
}
|
|
60022
|
+
if (agentNames.includes(value)) {
|
|
60023
|
+
return { primaryNames: new Set([value]), reason: "exact" };
|
|
60024
|
+
}
|
|
60025
|
+
const architects = collectArchitectRole();
|
|
60026
|
+
if (architects.length > 0) {
|
|
60027
|
+
return {
|
|
60028
|
+
primaryNames: new Set(architects),
|
|
60029
|
+
reason: "fallback-architects",
|
|
60030
|
+
warning: `[swarm] default_agent '${value}' did not match any registered agent; falling back to architect-role primaries: ${architects.join(", ")}.`
|
|
60031
|
+
};
|
|
60032
|
+
}
|
|
60033
|
+
const first = agentNames[0];
|
|
60034
|
+
return {
|
|
60035
|
+
primaryNames: new Set([first]),
|
|
60036
|
+
reason: "fallback-first",
|
|
60037
|
+
warning: `[swarm] default_agent '${value}' did not match any registered agent and no architect-role agents are registered; falling back to '${first}' as primary.`
|
|
60038
|
+
};
|
|
60039
|
+
}
|
|
59989
60040
|
function getAgentConfigs(config3, directory, sessionId) {
|
|
59990
60041
|
const agents = createAgents(config3);
|
|
59991
60042
|
const toolFilterEnabled = config3?.tool_filter?.enabled ?? true;
|
|
@@ -59993,21 +60044,29 @@ function getAgentConfigs(config3, directory, sessionId) {
|
|
|
59993
60044
|
const quiet = config3?.quiet ?? true;
|
|
59994
60045
|
const warnedMissingWhitelist = new Set;
|
|
59995
60046
|
const agentToolSnapshot = {};
|
|
60047
|
+
const resolution = resolvePrimaryAgentNames(agents.map((a) => a.name), config3?.default_agent);
|
|
60048
|
+
if (resolution.warning) {
|
|
60049
|
+
if (!quiet) {
|
|
60050
|
+
console.warn(resolution.warning);
|
|
60051
|
+
} else {
|
|
60052
|
+
addDeferredWarning(resolution.warning);
|
|
60053
|
+
}
|
|
60054
|
+
}
|
|
60055
|
+
if (agents.length > 0 && resolution.primaryNames.size === 0) {
|
|
60056
|
+
const generated = agents.map((a) => a.name).join(", ");
|
|
60057
|
+
const diagnostic = `[swarm] DIAGNOSTIC: ${agents.length} generated agents but zero primaries. Likely cause: a regression in resolvePrimaryAgentNames. Generated: ${generated}.`;
|
|
60058
|
+
if (!quiet) {
|
|
60059
|
+
console.warn(diagnostic);
|
|
60060
|
+
} else {
|
|
60061
|
+
addDeferredWarning(diagnostic);
|
|
60062
|
+
}
|
|
60063
|
+
}
|
|
59996
60064
|
const result = Object.fromEntries(agents.map((agent) => {
|
|
59997
60065
|
const sdkConfig = {
|
|
59998
60066
|
...agent.config,
|
|
59999
60067
|
description: agent.description
|
|
60000
60068
|
};
|
|
60001
|
-
|
|
60002
|
-
if (defaultAgent !== "architect" && !ALL_AGENT_NAMES.includes(defaultAgent)) {
|
|
60003
|
-
if (!quiet) {
|
|
60004
|
-
console.warn(`[swarm] Invalid default_agent '${defaultAgent}' — falling back to 'architect'. Valid values: ${ALL_AGENT_NAMES.join(", ")}`);
|
|
60005
|
-
} else {
|
|
60006
|
-
addDeferredWarning(`[swarm] Invalid default_agent '${defaultAgent}' — falling back to 'architect'. Valid values: ${ALL_AGENT_NAMES.join(", ")}`);
|
|
60007
|
-
}
|
|
60008
|
-
defaultAgent = "architect";
|
|
60009
|
-
}
|
|
60010
|
-
const isPrimaryAgent = agent.name === defaultAgent;
|
|
60069
|
+
const isPrimaryAgent = resolution.primaryNames.has(agent.name);
|
|
60011
60070
|
if (isPrimaryAgent) {
|
|
60012
60071
|
sdkConfig.mode = "primary";
|
|
60013
60072
|
sdkConfig.permission = { task: "allow" };
|
|
@@ -88288,6 +88347,10 @@ init_state();
|
|
|
88288
88347
|
function slugify2(str) {
|
|
88289
88348
|
return str.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_");
|
|
88290
88349
|
}
|
|
88350
|
+
var GENERATE_MUTANTS_TIMEOUT_MS = 90000;
|
|
88351
|
+
var _internals = {
|
|
88352
|
+
timeoutMs: GENERATE_MUTANTS_TIMEOUT_MS
|
|
88353
|
+
};
|
|
88291
88354
|
function extractJsonArray(text) {
|
|
88292
88355
|
const trimmed = text.trim();
|
|
88293
88356
|
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
@@ -88319,23 +88382,24 @@ async function generateMutants(files, ctx) {
|
|
|
88319
88382
|
}
|
|
88320
88383
|
};
|
|
88321
88384
|
try {
|
|
88322
|
-
const
|
|
88323
|
-
|
|
88324
|
-
|
|
88325
|
-
|
|
88326
|
-
|
|
88327
|
-
|
|
88328
|
-
|
|
88329
|
-
|
|
88330
|
-
|
|
88331
|
-
|
|
88332
|
-
|
|
88333
|
-
|
|
88334
|
-
|
|
88335
|
-
|
|
88336
|
-
|
|
88337
|
-
|
|
88338
|
-
|
|
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(", ")}
|
|
88339
88403
|
|
|
88340
88404
|
Return a JSON array where each element has:
|
|
88341
88405
|
{ id, filePath, functionName, mutationType, patch, lineNumber }
|
|
@@ -88346,53 +88410,55 @@ Return a JSON array where each element has:
|
|
|
88346
88410
|
- Generate 3-5 mutations per function
|
|
88347
88411
|
|
|
88348
88412
|
Return ONLY a valid JSON array. No markdown, no code fences, no explanation. Start your response with [ and end with ].`;
|
|
88349
|
-
|
|
88350
|
-
|
|
88351
|
-
|
|
88352
|
-
|
|
88353
|
-
|
|
88354
|
-
|
|
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 [];
|
|
88355
88424
|
}
|
|
88356
|
-
|
|
88357
|
-
|
|
88358
|
-
console.warn(`[generateMutants] LLM prompt failed: ${JSON.stringify(promptResult.error)}; returning empty patch set`);
|
|
88359
|
-
return [];
|
|
88360
|
-
}
|
|
88361
|
-
const textParts = promptResult.data.parts.filter((p) => p.type === "text");
|
|
88362
|
-
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(`
|
|
88363
88427
|
`);
|
|
88364
|
-
|
|
88365
|
-
|
|
88366
|
-
|
|
88367
|
-
|
|
88368
|
-
|
|
88369
|
-
|
|
88370
|
-
|
|
88371
|
-
|
|
88372
|
-
}
|
|
88373
|
-
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
88374
|
-
return [];
|
|
88375
|
-
}
|
|
88376
|
-
const patches = [];
|
|
88377
|
-
for (const item of parsed) {
|
|
88378
|
-
if (typeof item !== "object" || item === null || typeof item.filePath !== "string" || typeof item.functionName !== "string" || typeof item.mutationType !== "string" || typeof item.patch !== "string") {
|
|
88379
|
-
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 [];
|
|
88380
88436
|
}
|
|
88381
|
-
|
|
88382
|
-
|
|
88383
|
-
|
|
88384
|
-
const
|
|
88385
|
-
const
|
|
88386
|
-
|
|
88387
|
-
|
|
88388
|
-
|
|
88389
|
-
|
|
88390
|
-
|
|
88391
|
-
|
|
88392
|
-
|
|
88393
|
-
|
|
88394
|
-
|
|
88395
|
-
|
|
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"));
|
|
88396
88462
|
return patches;
|
|
88397
88463
|
} catch (error93) {
|
|
88398
88464
|
console.warn(`[generateMutants] LLM call failed: ${error93 instanceof Error ? error93.message : String(error93)}; returning empty patch set`);
|
|
@@ -88707,6 +88773,12 @@ async function batchCheckEquivalence(patches, llmJudge) {
|
|
|
88707
88773
|
var MUTATION_TIMEOUT_MS = 30000;
|
|
88708
88774
|
var TOTAL_BUDGET_MS = 300000;
|
|
88709
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
|
+
}
|
|
88710
88782
|
async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
88711
88783
|
const startTime = Date.now();
|
|
88712
88784
|
let outcome = "survived";
|
|
@@ -88733,7 +88805,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
88733
88805
|
};
|
|
88734
88806
|
}
|
|
88735
88807
|
try {
|
|
88736
|
-
const applyResult = spawnSync3("git",
|
|
88808
|
+
const applyResult = spawnSync3("git", buildGitApplyArgs(patchFile), {
|
|
88737
88809
|
cwd: workingDir,
|
|
88738
88810
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
88739
88811
|
stdio: "pipe"
|
|
@@ -88795,7 +88867,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
88795
88867
|
} finally {
|
|
88796
88868
|
if (patchFile) {
|
|
88797
88869
|
try {
|
|
88798
|
-
const revertResult = spawnSync3("git",
|
|
88870
|
+
const revertResult = spawnSync3("git", buildGitRevertArgs(patchFile), {
|
|
88799
88871
|
cwd: workingDir,
|
|
88800
88872
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
88801
88873
|
stdio: "pipe"
|
|
@@ -89622,7 +89694,7 @@ import * as path105 from "node:path";
|
|
|
89622
89694
|
init_bun_compat();
|
|
89623
89695
|
import * as fs84 from "node:fs";
|
|
89624
89696
|
import * as path104 from "node:path";
|
|
89625
|
-
var
|
|
89697
|
+
var _internals2 = { bunSpawn };
|
|
89626
89698
|
var _swarmGitExcludedChecked = false;
|
|
89627
89699
|
function fileCoversSwarm(content) {
|
|
89628
89700
|
for (const rawLine of content.split(`
|
|
@@ -89649,7 +89721,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89649
89721
|
_swarmGitExcludedChecked = true;
|
|
89650
89722
|
const { quiet = false } = options;
|
|
89651
89723
|
try {
|
|
89652
|
-
const gitRootProc =
|
|
89724
|
+
const gitRootProc = _internals2.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
|
|
89653
89725
|
let gitRootExitCode;
|
|
89654
89726
|
let gitRootOutput;
|
|
89655
89727
|
try {
|
|
@@ -89667,7 +89739,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89667
89739
|
const gitRoot = gitRootOutput.trim();
|
|
89668
89740
|
if (!gitRoot)
|
|
89669
89741
|
return;
|
|
89670
|
-
const excludePathProc =
|
|
89742
|
+
const excludePathProc = _internals2.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
|
|
89671
89743
|
let excludePathExitCode;
|
|
89672
89744
|
let excludePathRaw;
|
|
89673
89745
|
try {
|
|
@@ -89686,7 +89758,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89686
89758
|
if (!excludeRelPath)
|
|
89687
89759
|
return;
|
|
89688
89760
|
const excludePath = path104.isAbsolute(excludeRelPath) ? excludeRelPath : path104.join(directory, excludeRelPath);
|
|
89689
|
-
const checkIgnoreProc =
|
|
89761
|
+
const checkIgnoreProc = _internals2.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
|
|
89690
89762
|
let checkIgnoreExitCode;
|
|
89691
89763
|
try {
|
|
89692
89764
|
checkIgnoreExitCode = await checkIgnoreProc.exited;
|
|
@@ -89713,7 +89785,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89713
89785
|
}
|
|
89714
89786
|
} catch {}
|
|
89715
89787
|
}
|
|
89716
|
-
const trackedProc =
|
|
89788
|
+
const trackedProc = _internals2.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
|
|
89717
89789
|
let trackedExitCode;
|
|
89718
89790
|
let trackedOutput;
|
|
89719
89791
|
try {
|
|
@@ -89738,7 +89810,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89738
89810
|
}
|
|
89739
89811
|
|
|
89740
89812
|
// src/hooks/diff-scope.ts
|
|
89741
|
-
var
|
|
89813
|
+
var _internals3 = { bunSpawn };
|
|
89742
89814
|
function getDeclaredScope(taskId, directory) {
|
|
89743
89815
|
try {
|
|
89744
89816
|
const planPath = path105.join(directory, ".swarm", "plan.json");
|
|
@@ -89773,7 +89845,7 @@ var GIT_DIFF_SPAWN_OPTIONS = {
|
|
|
89773
89845
|
};
|
|
89774
89846
|
async function getChangedFiles(directory) {
|
|
89775
89847
|
try {
|
|
89776
|
-
const proc =
|
|
89848
|
+
const proc = _internals3.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
|
|
89777
89849
|
cwd: directory,
|
|
89778
89850
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
89779
89851
|
});
|
|
@@ -89790,7 +89862,7 @@ async function getChangedFiles(directory) {
|
|
|
89790
89862
|
return stdout.trim().split(`
|
|
89791
89863
|
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
89792
89864
|
}
|
|
89793
|
-
const proc2 =
|
|
89865
|
+
const proc2 = _internals3.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
|
|
89794
89866
|
cwd: directory,
|
|
89795
89867
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
89796
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",
|