opencode-swarm 7.3.5 → 7.3.7
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/hooks/full-auto-intercept.d.ts +24 -0
- package/dist/index.js +145 -78
- 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.7",
|
|
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",
|
|
@@ -7,7 +7,31 @@
|
|
|
7
7
|
* This hook runs as a chat.message transform — it inspects the architect's output
|
|
8
8
|
* and injects the critic's autonomous oversight response when escalation is detected.
|
|
9
9
|
*/
|
|
10
|
+
import type { OpencodeClient } from '@opencode-ai/sdk';
|
|
11
|
+
import type { AgentDefinition } from '../agents/architect.js';
|
|
10
12
|
import type { PluginConfig } from '../config';
|
|
13
|
+
import type { AgentSessionState } from '../state.js';
|
|
14
|
+
/**
|
|
15
|
+
* Test-only dependency-injection seam. Production code accesses state through
|
|
16
|
+
* `_internals.*` so tests can swap individual functions on this object without
|
|
17
|
+
* resorting to `mock.module('../../../src/state.js', ...)`, which leaks across
|
|
18
|
+
* test files in Bun's shared test-runner process and contaminates unrelated
|
|
19
|
+
* suites (see `gitignore-warning.ts:_internals` and `diff-scope.ts:_internals`
|
|
20
|
+
* for the pattern rationale). Tests should restore overridden properties in
|
|
21
|
+
* `afterEach`.
|
|
22
|
+
*
|
|
23
|
+
* All fields default to `null`. In production the lazy loaders (`_loadState`,
|
|
24
|
+
* `_loadCritic`) are used as fallbacks. In tests, set a non-null override
|
|
25
|
+
* before calling the hook to bypass the real module entirely.
|
|
26
|
+
*/
|
|
27
|
+
export declare const _internals: {
|
|
28
|
+
hasActiveFullAuto: ((sessionId?: string) => boolean) | null;
|
|
29
|
+
ensureAgentSession: ((sessionId: string) => AgentSessionState) | null;
|
|
30
|
+
swarmState: {
|
|
31
|
+
opencodeClient: OpencodeClient | null;
|
|
32
|
+
} | null;
|
|
33
|
+
createCriticAutonomousOversightAgent: ((model: string, customAppendPrompt?: string) => AgentDefinition) | null;
|
|
34
|
+
};
|
|
11
35
|
interface MessageWithParts {
|
|
12
36
|
info: {
|
|
13
37
|
role: string;
|
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.7",
|
|
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",
|
|
@@ -58236,6 +58236,18 @@ ${HARD_RULES}
|
|
|
58236
58236
|
});
|
|
58237
58237
|
|
|
58238
58238
|
// src/agents/critic.ts
|
|
58239
|
+
var exports_critic = {};
|
|
58240
|
+
__export(exports_critic, {
|
|
58241
|
+
parseSoundingBoardResponse: () => parseSoundingBoardResponse,
|
|
58242
|
+
createCriticDriftVerifierAgent: () => createCriticDriftVerifierAgent,
|
|
58243
|
+
createCriticAutonomousOversightAgent: () => createCriticAutonomousOversightAgent,
|
|
58244
|
+
createCriticAgent: () => createCriticAgent,
|
|
58245
|
+
SOUNDING_BOARD_PROMPT: () => SOUNDING_BOARD_PROMPT,
|
|
58246
|
+
PLAN_CRITIC_PROMPT: () => PLAN_CRITIC_PROMPT,
|
|
58247
|
+
PHASE_DRIFT_VERIFIER_PROMPT: () => PHASE_DRIFT_VERIFIER_PROMPT,
|
|
58248
|
+
HALLUCINATION_VERIFIER_PROMPT: () => HALLUCINATION_VERIFIER_PROMPT,
|
|
58249
|
+
AUTONOMOUS_OVERSIGHT_PROMPT: () => AUTONOMOUS_OVERSIGHT_PROMPT
|
|
58250
|
+
});
|
|
58239
58251
|
function parseSoundingBoardResponse(raw) {
|
|
58240
58252
|
if (typeof raw !== "string" || raw.trim().length === 0)
|
|
58241
58253
|
return null;
|
|
@@ -58301,6 +58313,25 @@ ${customAppendPrompt}` : rolePrompt;
|
|
|
58301
58313
|
}
|
|
58302
58314
|
};
|
|
58303
58315
|
}
|
|
58316
|
+
function createCriticDriftVerifierAgent(model, customAppendPrompt) {
|
|
58317
|
+
const prompt = customAppendPrompt ? `${PHASE_DRIFT_VERIFIER_PROMPT}
|
|
58318
|
+
|
|
58319
|
+
${customAppendPrompt}` : PHASE_DRIFT_VERIFIER_PROMPT;
|
|
58320
|
+
return {
|
|
58321
|
+
name: "critic",
|
|
58322
|
+
description: "Phase drift verifier. Independently verifies that every task in a completed phase was actually implemented as specified.",
|
|
58323
|
+
config: {
|
|
58324
|
+
model,
|
|
58325
|
+
temperature: 0.1,
|
|
58326
|
+
prompt,
|
|
58327
|
+
tools: {
|
|
58328
|
+
write: false,
|
|
58329
|
+
edit: false,
|
|
58330
|
+
patch: false
|
|
58331
|
+
}
|
|
58332
|
+
}
|
|
58333
|
+
};
|
|
58334
|
+
}
|
|
58304
58335
|
function createCriticAutonomousOversightAgent(model, customAppendPrompt) {
|
|
58305
58336
|
const prompt = customAppendPrompt ? `${AUTONOMOUS_OVERSIGHT_PROMPT}
|
|
58306
58337
|
|
|
@@ -66200,10 +66231,29 @@ function createDelegationTrackerHook(config3, guardrailsEnabled = true) {
|
|
|
66200
66231
|
// src/hooks/full-auto-intercept.ts
|
|
66201
66232
|
init_schema();
|
|
66202
66233
|
init_file_locks();
|
|
66203
|
-
init_state();
|
|
66204
66234
|
init_telemetry();
|
|
66205
66235
|
init_utils2();
|
|
66206
66236
|
import * as fs35 from "node:fs";
|
|
66237
|
+
var _stateCache = null;
|
|
66238
|
+
async function _loadState() {
|
|
66239
|
+
if (_stateCache === null) {
|
|
66240
|
+
_stateCache = await Promise.resolve().then(() => (init_state(), exports_state));
|
|
66241
|
+
}
|
|
66242
|
+
return _stateCache;
|
|
66243
|
+
}
|
|
66244
|
+
var _criticCache = null;
|
|
66245
|
+
async function _loadCritic() {
|
|
66246
|
+
if (_criticCache === null) {
|
|
66247
|
+
_criticCache = await Promise.resolve().then(() => exports_critic);
|
|
66248
|
+
}
|
|
66249
|
+
return _criticCache;
|
|
66250
|
+
}
|
|
66251
|
+
var _internals = {
|
|
66252
|
+
hasActiveFullAuto: null,
|
|
66253
|
+
ensureAgentSession: null,
|
|
66254
|
+
swarmState: null,
|
|
66255
|
+
createCriticAutonomousOversightAgent: null
|
|
66256
|
+
};
|
|
66207
66257
|
var END_OF_SENTENCE_QUESTION_PATTERN = /\?\s*$/;
|
|
66208
66258
|
var PHASE_COMPLETION_PATTERNS = [
|
|
66209
66259
|
/Ready for Phase (?:\d+|\[?N\+1\]?)\??/i,
|
|
@@ -66510,7 +66560,8 @@ Critic reasoning: ${criticResult.reasoning}`
|
|
|
66510
66560
|
}
|
|
66511
66561
|
}
|
|
66512
66562
|
async function dispatchCriticAndWriteEvent(directory, architectOutput, criticContext, criticModel, escalationType, interactionCount, deadlockCount, oversightAgentName) {
|
|
66513
|
-
const
|
|
66563
|
+
const swarmState2 = _internals.swarmState ?? (await _loadState()).swarmState;
|
|
66564
|
+
const client = swarmState2.opencodeClient;
|
|
66514
66565
|
if (!client) {
|
|
66515
66566
|
warn("[full-auto-intercept] No opencodeClient — critic dispatch skipped (fallback to PENDING)");
|
|
66516
66567
|
const result = {
|
|
@@ -66524,7 +66575,8 @@ async function dispatchCriticAndWriteEvent(directory, architectOutput, criticCon
|
|
|
66524
66575
|
await writeAutoOversightEvent(directory, architectOutput, result.verdict, result.reasoning, result.evidenceChecked, interactionCount, deadlockCount, escalationType);
|
|
66525
66576
|
return result;
|
|
66526
66577
|
}
|
|
66527
|
-
const
|
|
66578
|
+
const createCriticFn = _internals.createCriticAutonomousOversightAgent ?? (await _loadCritic()).createCriticAutonomousOversightAgent;
|
|
66579
|
+
const oversightAgent = createCriticFn(criticModel, criticContext);
|
|
66528
66580
|
log(`[full-auto-intercept] Dispatching critic: ${oversightAgent.name} using model ${criticModel}`);
|
|
66529
66581
|
let ephemeralSessionId;
|
|
66530
66582
|
const cleanup = () => {
|
|
@@ -66630,11 +66682,12 @@ function createFullAutoInterceptHook(config3, directory) {
|
|
|
66630
66682
|
if (!architectText)
|
|
66631
66683
|
return;
|
|
66632
66684
|
const sessionID = architectMessage.info?.sessionID;
|
|
66633
|
-
|
|
66685
|
+
const hasActiveFullAuto2 = _internals.hasActiveFullAuto ?? (await _loadState()).hasActiveFullAuto;
|
|
66686
|
+
if (!hasActiveFullAuto2(sessionID))
|
|
66634
66687
|
return;
|
|
66688
|
+
const ensureAgentSession2 = _internals.ensureAgentSession ?? (await _loadState()).ensureAgentSession;
|
|
66635
66689
|
let session = null;
|
|
66636
66690
|
if (sessionID) {
|
|
66637
|
-
const { ensureAgentSession: ensureAgentSession2 } = await Promise.resolve().then(() => (init_state(), exports_state));
|
|
66638
66691
|
session = ensureAgentSession2(sessionID);
|
|
66639
66692
|
}
|
|
66640
66693
|
if (session) {
|
|
@@ -66672,7 +66725,8 @@ function createFullAutoInterceptHook(config3, directory) {
|
|
|
66672
66725
|
log(`[full-auto-intercept] Escalation detected (${escalationType}) — triggering autonomous oversight`);
|
|
66673
66726
|
const criticContext = buildCriticContext(architectText, escalationType);
|
|
66674
66727
|
const criticModel = fullAutoConfig.critic_model ?? "claude-sonnet-4-20250514";
|
|
66675
|
-
const
|
|
66728
|
+
const createCriticFn = _internals.createCriticAutonomousOversightAgent ?? (await _loadCritic()).createCriticAutonomousOversightAgent;
|
|
66729
|
+
const oversightAgent = createCriticFn(criticModel, criticContext);
|
|
66676
66730
|
const architectAgent = architectMessage.info?.agent;
|
|
66677
66731
|
const resolvedOversightAgentName = resolveOversightAgentName(architectAgent);
|
|
66678
66732
|
const dispatchAgentName = resolvedOversightAgentName && resolvedOversightAgentName.length > 0 ? resolvedOversightAgentName : "critic_oversight";
|
|
@@ -88347,6 +88401,10 @@ init_state();
|
|
|
88347
88401
|
function slugify2(str) {
|
|
88348
88402
|
return str.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_");
|
|
88349
88403
|
}
|
|
88404
|
+
var GENERATE_MUTANTS_TIMEOUT_MS = 90000;
|
|
88405
|
+
var _internals2 = {
|
|
88406
|
+
timeoutMs: GENERATE_MUTANTS_TIMEOUT_MS
|
|
88407
|
+
};
|
|
88350
88408
|
function extractJsonArray(text) {
|
|
88351
88409
|
const trimmed = text.trim();
|
|
88352
88410
|
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
@@ -88378,23 +88436,24 @@ async function generateMutants(files, ctx) {
|
|
|
88378
88436
|
}
|
|
88379
88437
|
};
|
|
88380
88438
|
try {
|
|
88381
|
-
const
|
|
88382
|
-
|
|
88383
|
-
|
|
88384
|
-
|
|
88385
|
-
|
|
88386
|
-
|
|
88387
|
-
|
|
88388
|
-
|
|
88389
|
-
|
|
88390
|
-
|
|
88391
|
-
|
|
88392
|
-
|
|
88393
|
-
|
|
88394
|
-
|
|
88395
|
-
|
|
88396
|
-
|
|
88397
|
-
|
|
88439
|
+
const patches = await withTimeout((async () => {
|
|
88440
|
+
const createResult = await client.session.create({
|
|
88441
|
+
query: { directory }
|
|
88442
|
+
});
|
|
88443
|
+
if (!createResult.data) {
|
|
88444
|
+
console.warn(`[generateMutants] Failed to create session: ${JSON.stringify(createResult.error)}; returning empty patch set`);
|
|
88445
|
+
return [];
|
|
88446
|
+
}
|
|
88447
|
+
ephemeralSessionId = createResult.data.id;
|
|
88448
|
+
const mutationTypes = [
|
|
88449
|
+
"off-by-one",
|
|
88450
|
+
"null-substitution",
|
|
88451
|
+
"operator-swap",
|
|
88452
|
+
"guard-removal",
|
|
88453
|
+
"branch-swap",
|
|
88454
|
+
"side-effect-deletion"
|
|
88455
|
+
].join(", ");
|
|
88456
|
+
const promptText = `Generate mutation testing patches for the following files: ${files.join(", ")}
|
|
88398
88457
|
|
|
88399
88458
|
Return a JSON array where each element has:
|
|
88400
88459
|
{ id, filePath, functionName, mutationType, patch, lineNumber }
|
|
@@ -88405,53 +88464,55 @@ Return a JSON array where each element has:
|
|
|
88405
88464
|
- Generate 3-5 mutations per function
|
|
88406
88465
|
|
|
88407
88466
|
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
|
-
|
|
88467
|
+
const promptResult = await client.session.prompt({
|
|
88468
|
+
path: { id: ephemeralSessionId },
|
|
88469
|
+
body: {
|
|
88470
|
+
agent: undefined,
|
|
88471
|
+
tools: { write: false, edit: false, patch: false },
|
|
88472
|
+
parts: [{ type: "text", text: promptText }]
|
|
88473
|
+
}
|
|
88474
|
+
});
|
|
88475
|
+
if (!promptResult.data) {
|
|
88476
|
+
console.warn(`[generateMutants] LLM prompt failed: ${JSON.stringify(promptResult.error)}; returning empty patch set`);
|
|
88477
|
+
return [];
|
|
88414
88478
|
}
|
|
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(`
|
|
88479
|
+
const textParts = promptResult.data.parts.filter((p) => p.type === "text");
|
|
88480
|
+
const rawText = textParts.map((p) => p.text).join(`
|
|
88422
88481
|
`);
|
|
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;
|
|
88482
|
+
let parsed;
|
|
88483
|
+
try {
|
|
88484
|
+
parsed = JSON.parse(extractJsonArray(rawText));
|
|
88485
|
+
} catch (error93) {
|
|
88486
|
+
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
88487
|
+
const hint = msg.includes("EOF") || msg.includes("Unexpected end") ? " (response appears truncated — LLM may have hit an output token limit)" : "";
|
|
88488
|
+
console.warn(`[generateMutants] Failed to parse LLM response as MutationPatch[]: ${msg}${hint}; returning empty patch set`);
|
|
88489
|
+
return [];
|
|
88439
88490
|
}
|
|
88440
|
-
|
|
88441
|
-
|
|
88442
|
-
|
|
88443
|
-
const
|
|
88444
|
-
const
|
|
88445
|
-
|
|
88446
|
-
|
|
88447
|
-
|
|
88448
|
-
|
|
88449
|
-
|
|
88450
|
-
|
|
88451
|
-
|
|
88452
|
-
|
|
88453
|
-
|
|
88454
|
-
|
|
88491
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
88492
|
+
return [];
|
|
88493
|
+
}
|
|
88494
|
+
const patches2 = [];
|
|
88495
|
+
for (const item of parsed) {
|
|
88496
|
+
if (typeof item !== "object" || item === null || typeof item.filePath !== "string" || typeof item.functionName !== "string" || typeof item.mutationType !== "string" || typeof item.patch !== "string") {
|
|
88497
|
+
continue;
|
|
88498
|
+
}
|
|
88499
|
+
const mutationType = item.mutationType;
|
|
88500
|
+
const fileSlug = slugify2(item.filePath);
|
|
88501
|
+
const fnSlug = slugify2(item.functionName);
|
|
88502
|
+
const typeSlug = slugify2(mutationType);
|
|
88503
|
+
const idStr = typeof item.id === "string" ? item.id : "";
|
|
88504
|
+
const id = idStr.startsWith("mut-") ? idStr : `mut-${fileSlug}-${fnSlug}-${typeSlug}-${String(patches2.length + 1).padStart(3, "0")}`;
|
|
88505
|
+
patches2.push({
|
|
88506
|
+
id,
|
|
88507
|
+
filePath: item.filePath,
|
|
88508
|
+
functionName: item.functionName,
|
|
88509
|
+
mutationType,
|
|
88510
|
+
patch: item.patch,
|
|
88511
|
+
lineNumber: typeof item.lineNumber === "number" ? item.lineNumber : undefined
|
|
88512
|
+
});
|
|
88513
|
+
}
|
|
88514
|
+
return patches2;
|
|
88515
|
+
})(), _internals2.timeoutMs, new Error("generateMutants: LLM call timed out"));
|
|
88455
88516
|
return patches;
|
|
88456
88517
|
} catch (error93) {
|
|
88457
88518
|
console.warn(`[generateMutants] LLM call failed: ${error93 instanceof Error ? error93.message : String(error93)}; returning empty patch set`);
|
|
@@ -88766,6 +88827,12 @@ async function batchCheckEquivalence(patches, llmJudge) {
|
|
|
88766
88827
|
var MUTATION_TIMEOUT_MS = 30000;
|
|
88767
88828
|
var TOTAL_BUDGET_MS = 300000;
|
|
88768
88829
|
var GIT_APPLY_TIMEOUT_MS = 5000;
|
|
88830
|
+
function buildGitApplyArgs(patchFile) {
|
|
88831
|
+
return ["apply", "--ignore-whitespace", "--", patchFile];
|
|
88832
|
+
}
|
|
88833
|
+
function buildGitRevertArgs(patchFile) {
|
|
88834
|
+
return ["apply", "-R", "--ignore-whitespace", "--", patchFile];
|
|
88835
|
+
}
|
|
88769
88836
|
async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
88770
88837
|
const startTime = Date.now();
|
|
88771
88838
|
let outcome = "survived";
|
|
@@ -88792,7 +88859,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
88792
88859
|
};
|
|
88793
88860
|
}
|
|
88794
88861
|
try {
|
|
88795
|
-
const applyResult = spawnSync3("git",
|
|
88862
|
+
const applyResult = spawnSync3("git", buildGitApplyArgs(patchFile), {
|
|
88796
88863
|
cwd: workingDir,
|
|
88797
88864
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
88798
88865
|
stdio: "pipe"
|
|
@@ -88854,7 +88921,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
88854
88921
|
} finally {
|
|
88855
88922
|
if (patchFile) {
|
|
88856
88923
|
try {
|
|
88857
|
-
const revertResult = spawnSync3("git",
|
|
88924
|
+
const revertResult = spawnSync3("git", buildGitRevertArgs(patchFile), {
|
|
88858
88925
|
cwd: workingDir,
|
|
88859
88926
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
88860
88927
|
stdio: "pipe"
|
|
@@ -89681,7 +89748,7 @@ import * as path105 from "node:path";
|
|
|
89681
89748
|
init_bun_compat();
|
|
89682
89749
|
import * as fs84 from "node:fs";
|
|
89683
89750
|
import * as path104 from "node:path";
|
|
89684
|
-
var
|
|
89751
|
+
var _internals3 = { bunSpawn };
|
|
89685
89752
|
var _swarmGitExcludedChecked = false;
|
|
89686
89753
|
function fileCoversSwarm(content) {
|
|
89687
89754
|
for (const rawLine of content.split(`
|
|
@@ -89708,7 +89775,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89708
89775
|
_swarmGitExcludedChecked = true;
|
|
89709
89776
|
const { quiet = false } = options;
|
|
89710
89777
|
try {
|
|
89711
|
-
const gitRootProc =
|
|
89778
|
+
const gitRootProc = _internals3.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
|
|
89712
89779
|
let gitRootExitCode;
|
|
89713
89780
|
let gitRootOutput;
|
|
89714
89781
|
try {
|
|
@@ -89726,7 +89793,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89726
89793
|
const gitRoot = gitRootOutput.trim();
|
|
89727
89794
|
if (!gitRoot)
|
|
89728
89795
|
return;
|
|
89729
|
-
const excludePathProc =
|
|
89796
|
+
const excludePathProc = _internals3.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
|
|
89730
89797
|
let excludePathExitCode;
|
|
89731
89798
|
let excludePathRaw;
|
|
89732
89799
|
try {
|
|
@@ -89745,7 +89812,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89745
89812
|
if (!excludeRelPath)
|
|
89746
89813
|
return;
|
|
89747
89814
|
const excludePath = path104.isAbsolute(excludeRelPath) ? excludeRelPath : path104.join(directory, excludeRelPath);
|
|
89748
|
-
const checkIgnoreProc =
|
|
89815
|
+
const checkIgnoreProc = _internals3.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
|
|
89749
89816
|
let checkIgnoreExitCode;
|
|
89750
89817
|
try {
|
|
89751
89818
|
checkIgnoreExitCode = await checkIgnoreProc.exited;
|
|
@@ -89772,7 +89839,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89772
89839
|
}
|
|
89773
89840
|
} catch {}
|
|
89774
89841
|
}
|
|
89775
|
-
const trackedProc =
|
|
89842
|
+
const trackedProc = _internals3.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
|
|
89776
89843
|
let trackedExitCode;
|
|
89777
89844
|
let trackedOutput;
|
|
89778
89845
|
try {
|
|
@@ -89797,7 +89864,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
89797
89864
|
}
|
|
89798
89865
|
|
|
89799
89866
|
// src/hooks/diff-scope.ts
|
|
89800
|
-
var
|
|
89867
|
+
var _internals4 = { bunSpawn };
|
|
89801
89868
|
function getDeclaredScope(taskId, directory) {
|
|
89802
89869
|
try {
|
|
89803
89870
|
const planPath = path105.join(directory, ".swarm", "plan.json");
|
|
@@ -89832,7 +89899,7 @@ var GIT_DIFF_SPAWN_OPTIONS = {
|
|
|
89832
89899
|
};
|
|
89833
89900
|
async function getChangedFiles(directory) {
|
|
89834
89901
|
try {
|
|
89835
|
-
const proc =
|
|
89902
|
+
const proc = _internals4.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
|
|
89836
89903
|
cwd: directory,
|
|
89837
89904
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
89838
89905
|
});
|
|
@@ -89849,7 +89916,7 @@ async function getChangedFiles(directory) {
|
|
|
89849
89916
|
return stdout.trim().split(`
|
|
89850
89917
|
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
89851
89918
|
}
|
|
89852
|
-
const proc2 =
|
|
89919
|
+
const proc2 = _internals4.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
|
|
89853
89920
|
cwd: directory,
|
|
89854
89921
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
89855
89922
|
});
|
|
@@ -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.7",
|
|
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",
|