opencode-swarm 6.53.2 → 6.53.5
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 +386 -50
- package/dist/commands/close.d.ts +6 -2
- package/dist/commands/full-auto-config-guard.test.d.ts +7 -0
- package/dist/commands/full-auto-discoverability.test.d.ts +8 -0
- package/dist/commands/registry.d.ts +10 -0
- package/dist/hooks/full-auto-intercept.d.ts +1 -1
- package/dist/index.js +474 -73
- package/dist/services/handoff-service.d.ts +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31389,7 +31389,7 @@ var init_ledger = __esm(() => {
|
|
|
31389
31389
|
});
|
|
31390
31390
|
|
|
31391
31391
|
// src/plan/manager.ts
|
|
31392
|
-
import { existsSync as existsSync7, renameSync as renameSync4, unlinkSync } from "fs";
|
|
31392
|
+
import { copyFileSync, existsSync as existsSync7, renameSync as renameSync4, unlinkSync } from "fs";
|
|
31393
31393
|
import * as path11 from "path";
|
|
31394
31394
|
async function loadPlanJsonOnly(directory) {
|
|
31395
31395
|
const planJsonContent = await readSwarmFileAsync(directory, "plan.json");
|
|
@@ -31647,18 +31647,61 @@ async function savePlan(directory, plan, options) {
|
|
|
31647
31647
|
if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
|
|
31648
31648
|
const swarmDir2 = path11.resolve(directory, ".swarm");
|
|
31649
31649
|
const oldLedgerPath = path11.join(swarmDir2, "plan-ledger.jsonl");
|
|
31650
|
-
const
|
|
31650
|
+
const oldLedgerBackupPath = path11.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
|
|
31651
|
+
let backupExists = false;
|
|
31651
31652
|
if (existsSync7(oldLedgerPath)) {
|
|
31652
|
-
|
|
31653
|
-
|
|
31653
|
+
try {
|
|
31654
|
+
renameSync4(oldLedgerPath, oldLedgerBackupPath);
|
|
31655
|
+
backupExists = true;
|
|
31656
|
+
} catch (renameErr) {
|
|
31657
|
+
throw new Error(`[savePlan] Cannot reinitialize ledger: could not move old ledger aside (rename failed: ${renameErr instanceof Error ? renameErr.message : String(renameErr)}). The existing ledger has plan_id="${existingEvents[0].plan_id}" which does not match the current plan="${planId}". To proceed, close any programs that may have the ledger file open, or run /swarm reset-session to clear the ledger.`);
|
|
31658
|
+
}
|
|
31654
31659
|
}
|
|
31655
|
-
|
|
31656
|
-
|
|
31657
|
-
|
|
31658
|
-
|
|
31659
|
-
|
|
31660
|
+
let initSucceeded = false;
|
|
31661
|
+
if (backupExists) {
|
|
31662
|
+
try {
|
|
31663
|
+
await initLedger(directory, planId, planHashForInit);
|
|
31664
|
+
initSucceeded = true;
|
|
31665
|
+
} catch (initErr) {
|
|
31666
|
+
const errorMessage = String(initErr);
|
|
31667
|
+
if (errorMessage.includes("already initialized")) {
|
|
31668
|
+
try {
|
|
31669
|
+
if (existsSync7(oldLedgerBackupPath))
|
|
31670
|
+
unlinkSync(oldLedgerBackupPath);
|
|
31671
|
+
} catch {}
|
|
31672
|
+
} else {
|
|
31673
|
+
if (existsSync7(oldLedgerBackupPath)) {
|
|
31674
|
+
try {
|
|
31675
|
+
renameSync4(oldLedgerBackupPath, oldLedgerPath);
|
|
31676
|
+
} catch {
|
|
31677
|
+
copyFileSync(oldLedgerBackupPath, oldLedgerPath);
|
|
31678
|
+
try {
|
|
31679
|
+
unlinkSync(oldLedgerBackupPath);
|
|
31680
|
+
} catch {}
|
|
31681
|
+
}
|
|
31682
|
+
}
|
|
31683
|
+
throw initErr;
|
|
31684
|
+
}
|
|
31660
31685
|
}
|
|
31661
31686
|
}
|
|
31687
|
+
if (initSucceeded && backupExists) {
|
|
31688
|
+
const archivePath = path11.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
|
|
31689
|
+
try {
|
|
31690
|
+
renameSync4(oldLedgerBackupPath, archivePath);
|
|
31691
|
+
warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
|
|
31692
|
+
} catch (renameErr) {
|
|
31693
|
+
warn(`[savePlan] Could not archive old ledger (rename failed: ${renameErr instanceof Error ? renameErr.message : String(renameErr)}). Old ledger may still exist at ${oldLedgerBackupPath}.`);
|
|
31694
|
+
try {
|
|
31695
|
+
if (existsSync7(oldLedgerBackupPath))
|
|
31696
|
+
unlinkSync(oldLedgerBackupPath);
|
|
31697
|
+
} catch {}
|
|
31698
|
+
}
|
|
31699
|
+
} else if (!initSucceeded && backupExists) {
|
|
31700
|
+
try {
|
|
31701
|
+
if (existsSync7(oldLedgerBackupPath))
|
|
31702
|
+
unlinkSync(oldLedgerBackupPath);
|
|
31703
|
+
} catch {}
|
|
31704
|
+
}
|
|
31662
31705
|
}
|
|
31663
31706
|
}
|
|
31664
31707
|
const currentHash = computeCurrentPlanHash(directory);
|
|
@@ -32167,13 +32210,13 @@ __export(exports_co_change_analyzer, {
|
|
|
32167
32210
|
co_change_analyzer: () => co_change_analyzer,
|
|
32168
32211
|
buildCoChangeMatrix: () => buildCoChangeMatrix
|
|
32169
32212
|
});
|
|
32170
|
-
import * as
|
|
32213
|
+
import * as child_process3 from "child_process";
|
|
32171
32214
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
32172
32215
|
import { readdir as readdir2, readFile as readFile4, stat } from "fs/promises";
|
|
32173
32216
|
import * as path17 from "path";
|
|
32174
32217
|
import { promisify } from "util";
|
|
32175
32218
|
function getExecFileAsync() {
|
|
32176
|
-
return promisify(
|
|
32219
|
+
return promisify(child_process3.execFile);
|
|
32177
32220
|
}
|
|
32178
32221
|
async function parseGitLog(directory, maxCommits) {
|
|
32179
32222
|
const commitMap = new Map;
|
|
@@ -43290,6 +43333,52 @@ import { execFileSync } from "child_process";
|
|
|
43290
43333
|
import { promises as fs9 } from "fs";
|
|
43291
43334
|
import path14 from "path";
|
|
43292
43335
|
|
|
43336
|
+
// src/git/branch.ts
|
|
43337
|
+
init_logger();
|
|
43338
|
+
import * as child_process2 from "child_process";
|
|
43339
|
+
var GIT_TIMEOUT_MS2 = 30000;
|
|
43340
|
+
function gitExec2(args2, cwd) {
|
|
43341
|
+
const result = child_process2.spawnSync("git", args2, {
|
|
43342
|
+
cwd,
|
|
43343
|
+
encoding: "utf-8",
|
|
43344
|
+
timeout: GIT_TIMEOUT_MS2,
|
|
43345
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
43346
|
+
});
|
|
43347
|
+
if (result.status !== 0) {
|
|
43348
|
+
throw new Error(result.stderr || `git exited with ${result.status}`);
|
|
43349
|
+
}
|
|
43350
|
+
return result.stdout;
|
|
43351
|
+
}
|
|
43352
|
+
function isGitRepo2(cwd) {
|
|
43353
|
+
try {
|
|
43354
|
+
gitExec2(["rev-parse", "--git-dir"], cwd);
|
|
43355
|
+
return true;
|
|
43356
|
+
} catch {
|
|
43357
|
+
return false;
|
|
43358
|
+
}
|
|
43359
|
+
}
|
|
43360
|
+
function getCurrentBranch(cwd) {
|
|
43361
|
+
const output = gitExec2(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
43362
|
+
return output.trim();
|
|
43363
|
+
}
|
|
43364
|
+
function getDefaultBaseBranch(cwd) {
|
|
43365
|
+
try {
|
|
43366
|
+
gitExec2(["rev-parse", "--verify", "origin/main"], cwd);
|
|
43367
|
+
return "origin/main";
|
|
43368
|
+
} catch {
|
|
43369
|
+
try {
|
|
43370
|
+
gitExec2(["rev-parse", "--verify", "origin/master"], cwd);
|
|
43371
|
+
return "origin/master";
|
|
43372
|
+
} catch {
|
|
43373
|
+
return "origin/main";
|
|
43374
|
+
}
|
|
43375
|
+
}
|
|
43376
|
+
}
|
|
43377
|
+
function hasUncommittedChanges(cwd) {
|
|
43378
|
+
const status = gitExec2(["status", "--porcelain"], cwd);
|
|
43379
|
+
return status.trim().length > 0;
|
|
43380
|
+
}
|
|
43381
|
+
|
|
43293
43382
|
// src/hooks/knowledge-reader.ts
|
|
43294
43383
|
init_knowledge_store();
|
|
43295
43384
|
import { existsSync as existsSync5 } from "fs";
|
|
@@ -44681,8 +44770,28 @@ var write_retro = createSwarmTool({
|
|
|
44681
44770
|
});
|
|
44682
44771
|
|
|
44683
44772
|
// src/commands/close.ts
|
|
44773
|
+
var ARCHIVE_ARTIFACTS = [
|
|
44774
|
+
"plan.json",
|
|
44775
|
+
"plan.md",
|
|
44776
|
+
"context.md",
|
|
44777
|
+
"events.jsonl",
|
|
44778
|
+
"handoff.md",
|
|
44779
|
+
"handoff-prompt.md",
|
|
44780
|
+
"handoff-consumed.md",
|
|
44781
|
+
"escalation-report.md",
|
|
44782
|
+
"close-lessons.md"
|
|
44783
|
+
];
|
|
44784
|
+
var ACTIVE_STATE_TO_CLEAN = [
|
|
44785
|
+
"plan.md",
|
|
44786
|
+
"events.jsonl",
|
|
44787
|
+
"handoff.md",
|
|
44788
|
+
"handoff-prompt.md",
|
|
44789
|
+
"handoff-consumed.md",
|
|
44790
|
+
"escalation-report.md"
|
|
44791
|
+
];
|
|
44684
44792
|
async function handleCloseCommand(directory, args2) {
|
|
44685
44793
|
const planPath = validateSwarmPath(directory, "plan.json");
|
|
44794
|
+
const swarmDir = path14.join(directory, ".swarm");
|
|
44686
44795
|
let planExists = false;
|
|
44687
44796
|
let planData = {
|
|
44688
44797
|
title: path14.basename(directory) || "Ad-hoc session",
|
|
@@ -44696,13 +44805,14 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44696
44805
|
if (error93?.code !== "ENOENT") {
|
|
44697
44806
|
return `\u274C Failed to read plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
44698
44807
|
}
|
|
44699
|
-
const swarmDirExists = await fs9.access(
|
|
44808
|
+
const swarmDirExists = await fs9.access(swarmDir).then(() => true).catch(() => false);
|
|
44700
44809
|
if (!swarmDirExists) {
|
|
44701
44810
|
return `\u274C No .swarm/ directory found in ${directory}. Run /swarm close from the project root, or run /swarm plan first.`;
|
|
44702
44811
|
}
|
|
44703
44812
|
}
|
|
44704
44813
|
const phases = planData.phases ?? [];
|
|
44705
44814
|
const inProgressPhases = phases.filter((p) => p.status === "in_progress");
|
|
44815
|
+
const isForced = args2.includes("--force");
|
|
44706
44816
|
let planAlreadyDone = false;
|
|
44707
44817
|
if (planExists) {
|
|
44708
44818
|
planAlreadyDone = phases.length > 0 && phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
|
|
@@ -44719,7 +44829,7 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44719
44829
|
try {
|
|
44720
44830
|
retroResult = await executeWriteRetro({
|
|
44721
44831
|
phase: phase.id,
|
|
44722
|
-
summary:
|
|
44832
|
+
summary: isForced ? `Phase force-closed via /swarm close --force` : `Phase closed via /swarm close`,
|
|
44723
44833
|
task_count: Math.max(1, (phase.tasks ?? []).length),
|
|
44724
44834
|
task_complexity: "simple",
|
|
44725
44835
|
total_tool_calls: 0,
|
|
@@ -44747,7 +44857,7 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44747
44857
|
}
|
|
44748
44858
|
}
|
|
44749
44859
|
}
|
|
44750
|
-
const lessonsFilePath = path14.join(
|
|
44860
|
+
const lessonsFilePath = path14.join(swarmDir, "close-lessons.md");
|
|
44751
44861
|
let explicitLessons = [];
|
|
44752
44862
|
try {
|
|
44753
44863
|
const lessonsText = await fs9.readFile(lessonsFilePath, "utf-8");
|
|
@@ -44787,13 +44897,83 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44787
44897
|
console.warn("[close-command] Failed to write plan.json:", error93);
|
|
44788
44898
|
}
|
|
44789
44899
|
}
|
|
44900
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
44901
|
+
const archiveDir = path14.join(swarmDir, "archive", `swarm-${timestamp}`);
|
|
44902
|
+
let archiveResult = "";
|
|
44903
|
+
let archivedFileCount = 0;
|
|
44904
|
+
const archivedActiveStateFiles = new Set;
|
|
44905
|
+
try {
|
|
44906
|
+
await fs9.mkdir(archiveDir, { recursive: true });
|
|
44907
|
+
for (const artifact of ARCHIVE_ARTIFACTS) {
|
|
44908
|
+
const srcPath = path14.join(swarmDir, artifact);
|
|
44909
|
+
const destPath = path14.join(archiveDir, artifact);
|
|
44910
|
+
try {
|
|
44911
|
+
await fs9.copyFile(srcPath, destPath);
|
|
44912
|
+
archivedFileCount++;
|
|
44913
|
+
if (ACTIVE_STATE_TO_CLEAN.includes(artifact)) {
|
|
44914
|
+
archivedActiveStateFiles.add(artifact);
|
|
44915
|
+
}
|
|
44916
|
+
} catch {}
|
|
44917
|
+
}
|
|
44918
|
+
const evidenceDir = path14.join(swarmDir, "evidence");
|
|
44919
|
+
const archiveEvidenceDir = path14.join(archiveDir, "evidence");
|
|
44920
|
+
try {
|
|
44921
|
+
const evidenceEntries = await fs9.readdir(evidenceDir);
|
|
44922
|
+
if (evidenceEntries.length > 0) {
|
|
44923
|
+
await fs9.mkdir(archiveEvidenceDir, { recursive: true });
|
|
44924
|
+
for (const entry of evidenceEntries) {
|
|
44925
|
+
const srcEntry = path14.join(evidenceDir, entry);
|
|
44926
|
+
const destEntry = path14.join(archiveEvidenceDir, entry);
|
|
44927
|
+
try {
|
|
44928
|
+
const stat = await fs9.stat(srcEntry);
|
|
44929
|
+
if (stat.isDirectory()) {
|
|
44930
|
+
await fs9.mkdir(destEntry, { recursive: true });
|
|
44931
|
+
const subEntries = await fs9.readdir(srcEntry);
|
|
44932
|
+
for (const sub of subEntries) {
|
|
44933
|
+
await fs9.copyFile(path14.join(srcEntry, sub), path14.join(destEntry, sub)).catch(() => {});
|
|
44934
|
+
}
|
|
44935
|
+
} else {
|
|
44936
|
+
await fs9.copyFile(srcEntry, destEntry);
|
|
44937
|
+
}
|
|
44938
|
+
archivedFileCount++;
|
|
44939
|
+
} catch {}
|
|
44940
|
+
}
|
|
44941
|
+
}
|
|
44942
|
+
} catch {}
|
|
44943
|
+
const sessionStatePath = path14.join(swarmDir, "session", "state.json");
|
|
44944
|
+
try {
|
|
44945
|
+
const archiveSessionDir = path14.join(archiveDir, "session");
|
|
44946
|
+
await fs9.mkdir(archiveSessionDir, { recursive: true });
|
|
44947
|
+
await fs9.copyFile(sessionStatePath, path14.join(archiveSessionDir, "state.json"));
|
|
44948
|
+
archivedFileCount++;
|
|
44949
|
+
} catch {}
|
|
44950
|
+
archiveResult = `Archived ${archivedFileCount} artifact(s) to .swarm/archive/swarm-${timestamp}/`;
|
|
44951
|
+
} catch (archiveError) {
|
|
44952
|
+
warnings.push(`Archive creation failed: ${archiveError instanceof Error ? archiveError.message : String(archiveError)}`);
|
|
44953
|
+
archiveResult = "Archive creation failed (see warnings)";
|
|
44954
|
+
}
|
|
44790
44955
|
try {
|
|
44791
44956
|
await archiveEvidence(directory, 30, 10);
|
|
44792
44957
|
} catch (error93) {
|
|
44793
44958
|
console.warn("[close-command] archiveEvidence error:", error93);
|
|
44794
44959
|
}
|
|
44795
|
-
const swarmDir = path14.join(directory, ".swarm");
|
|
44796
44960
|
let configBackupsRemoved = 0;
|
|
44961
|
+
const cleanedFiles = [];
|
|
44962
|
+
if (archivedActiveStateFiles.size > 0) {
|
|
44963
|
+
for (const artifact of ACTIVE_STATE_TO_CLEAN) {
|
|
44964
|
+
if (!archivedActiveStateFiles.has(artifact)) {
|
|
44965
|
+
warnings.push(`Preserved ${artifact} because it was not successfully archived.`);
|
|
44966
|
+
continue;
|
|
44967
|
+
}
|
|
44968
|
+
const filePath = path14.join(swarmDir, artifact);
|
|
44969
|
+
try {
|
|
44970
|
+
await fs9.unlink(filePath);
|
|
44971
|
+
cleanedFiles.push(artifact);
|
|
44972
|
+
} catch {}
|
|
44973
|
+
}
|
|
44974
|
+
} else {
|
|
44975
|
+
warnings.push("Skipped active-state cleanup because no active-state files were archived. Files preserved to prevent data loss.");
|
|
44976
|
+
}
|
|
44797
44977
|
try {
|
|
44798
44978
|
const swarmFiles = await fs9.readdir(swarmDir);
|
|
44799
44979
|
const configBackups = swarmFiles.filter((f) => f.startsWith("config-backup-") && f.endsWith(".json"));
|
|
@@ -44804,13 +44984,14 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44804
44984
|
} catch {}
|
|
44805
44985
|
}
|
|
44806
44986
|
} catch {}
|
|
44807
|
-
const contextPath = path14.join(
|
|
44987
|
+
const contextPath = path14.join(swarmDir, "context.md");
|
|
44808
44988
|
const contextContent = [
|
|
44809
44989
|
"# Context",
|
|
44810
44990
|
"",
|
|
44811
44991
|
"## Status",
|
|
44812
44992
|
`Session closed after: ${projectName}`,
|
|
44813
44993
|
`Closed: ${new Date().toISOString()}`,
|
|
44994
|
+
`Finalization: ${isForced ? "forced" : planAlreadyDone ? "plan-already-done" : "normal"}`,
|
|
44814
44995
|
"No active plan. Next session starts fresh.",
|
|
44815
44996
|
""
|
|
44816
44997
|
].join(`
|
|
@@ -44823,46 +45004,112 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44823
45004
|
const pruneBranches = args2.includes("--prune-branches");
|
|
44824
45005
|
const prunedBranches = [];
|
|
44825
45006
|
const pruneErrors = [];
|
|
44826
|
-
|
|
45007
|
+
let gitAlignResult = "";
|
|
45008
|
+
const isGit = isGitRepo2(directory);
|
|
45009
|
+
if (isGit) {
|
|
44827
45010
|
try {
|
|
44828
|
-
const
|
|
44829
|
-
|
|
44830
|
-
|
|
44831
|
-
|
|
44832
|
-
})
|
|
44833
|
-
|
|
44834
|
-
|
|
44835
|
-
|
|
44836
|
-
|
|
44837
|
-
|
|
44838
|
-
|
|
44839
|
-
|
|
44840
|
-
|
|
44841
|
-
|
|
44842
|
-
|
|
44843
|
-
|
|
44844
|
-
|
|
45011
|
+
const currentBranch = getCurrentBranch(directory);
|
|
45012
|
+
if (currentBranch === "HEAD") {
|
|
45013
|
+
gitAlignResult = "Skipped git alignment: detached HEAD state";
|
|
45014
|
+
warnings.push("Repo is in detached HEAD state. Checkout a branch before starting a new swarm.");
|
|
45015
|
+
} else if (hasUncommittedChanges(directory)) {
|
|
45016
|
+
gitAlignResult = "Skipped git alignment: uncommitted changes in worktree";
|
|
45017
|
+
warnings.push("Uncommitted changes detected. Commit or stash before aligning to main.");
|
|
45018
|
+
} else {
|
|
45019
|
+
const baseBranch = getDefaultBaseBranch(directory);
|
|
45020
|
+
const localBase = baseBranch.replace(/^origin\//, "");
|
|
45021
|
+
if (currentBranch === localBase) {
|
|
45022
|
+
try {
|
|
45023
|
+
execFileSync("git", ["fetch", "origin", localBase], {
|
|
45024
|
+
cwd: directory,
|
|
45025
|
+
encoding: "utf-8",
|
|
45026
|
+
timeout: 30000,
|
|
45027
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
45028
|
+
});
|
|
45029
|
+
const mergeBase = execFileSync("git", ["merge-base", "HEAD", baseBranch], {
|
|
45030
|
+
cwd: directory,
|
|
45031
|
+
encoding: "utf-8",
|
|
45032
|
+
timeout: 1e4,
|
|
45033
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
45034
|
+
}).trim();
|
|
45035
|
+
const headSha = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
45036
|
+
cwd: directory,
|
|
45037
|
+
encoding: "utf-8",
|
|
45038
|
+
timeout: 1e4,
|
|
45039
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
45040
|
+
}).trim();
|
|
45041
|
+
if (mergeBase === headSha) {
|
|
45042
|
+
execFileSync("git", ["merge", "--ff-only", baseBranch], {
|
|
45043
|
+
cwd: directory,
|
|
45044
|
+
encoding: "utf-8",
|
|
45045
|
+
timeout: 30000,
|
|
45046
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
45047
|
+
});
|
|
45048
|
+
gitAlignResult = `Aligned to ${baseBranch} (fast-forward)`;
|
|
45049
|
+
} else {
|
|
45050
|
+
gitAlignResult = `On ${localBase} but cannot fast-forward to ${baseBranch} (diverged)`;
|
|
45051
|
+
warnings.push(`Local ${localBase} has diverged from ${baseBranch}. Manual merge/rebase needed.`);
|
|
45052
|
+
}
|
|
45053
|
+
} catch (fetchErr) {
|
|
45054
|
+
gitAlignResult = `Fetch from origin/${localBase} failed \u2014 remote may be unavailable`;
|
|
45055
|
+
warnings.push(`Git fetch failed: ${fetchErr instanceof Error ? fetchErr.message : String(fetchErr)}`);
|
|
45056
|
+
}
|
|
45057
|
+
} else {
|
|
45058
|
+
gitAlignResult = `On branch ${currentBranch}. Switch to ${localBase} manually when ready for a new swarm.`;
|
|
44845
45059
|
}
|
|
44846
45060
|
}
|
|
44847
|
-
} catch {
|
|
45061
|
+
} catch (gitError) {
|
|
45062
|
+
gitAlignResult = `Git alignment error: ${gitError instanceof Error ? gitError.message : String(gitError)}`;
|
|
45063
|
+
}
|
|
45064
|
+
if (pruneBranches) {
|
|
45065
|
+
try {
|
|
45066
|
+
const branchOutput = execFileSync("git", ["branch", "-vv"], {
|
|
45067
|
+
cwd: directory,
|
|
45068
|
+
encoding: "utf-8",
|
|
45069
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
45070
|
+
});
|
|
45071
|
+
const goneBranches = branchOutput.split(`
|
|
45072
|
+
`).filter((line) => line.includes(": gone]")).map((line) => line.trim().replace(/^[*+]\s+/, "").split(/\s+/)[0]).filter(Boolean);
|
|
45073
|
+
for (const branch of goneBranches) {
|
|
45074
|
+
try {
|
|
45075
|
+
execFileSync("git", ["branch", "-d", branch], {
|
|
45076
|
+
cwd: directory,
|
|
45077
|
+
encoding: "utf-8",
|
|
45078
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
45079
|
+
});
|
|
45080
|
+
prunedBranches.push(branch);
|
|
45081
|
+
} catch {
|
|
45082
|
+
pruneErrors.push(branch);
|
|
45083
|
+
}
|
|
45084
|
+
}
|
|
45085
|
+
} catch {}
|
|
45086
|
+
}
|
|
45087
|
+
} else {
|
|
45088
|
+
gitAlignResult = "Not a git repository \u2014 skipped git alignment";
|
|
44848
45089
|
}
|
|
44849
45090
|
const closeSummaryPath = validateSwarmPath(directory, "close-summary.md");
|
|
45091
|
+
const finalizationType = isForced ? "Forced closure" : planAlreadyDone ? "Plan already terminal \u2014 cleanup only" : "Normal finalization";
|
|
44850
45092
|
const actionsPerformed = [
|
|
44851
45093
|
...!planAlreadyDone && inProgressPhases.length > 0 ? ["- Wrote retrospectives for in-progress phases"] : [],
|
|
44852
|
-
|
|
45094
|
+
`- ${archiveResult}`,
|
|
45095
|
+
...cleanedFiles.length > 0 ? [
|
|
45096
|
+
`- Cleaned ${cleanedFiles.length} active-state file(s): ${cleanedFiles.join(", ")}`
|
|
45097
|
+
] : [],
|
|
44853
45098
|
"- Reset context.md for next session",
|
|
44854
45099
|
...configBackupsRemoved > 0 ? [`- Removed ${configBackupsRemoved} stale config backup file(s)`] : [],
|
|
44855
45100
|
...prunedBranches.length > 0 ? [
|
|
44856
45101
|
`- Pruned ${prunedBranches.length} stale local git branch(es): ${prunedBranches.join(", ")}`
|
|
44857
45102
|
] : [],
|
|
44858
45103
|
"- Cleared agent sessions and delegation chains",
|
|
44859
|
-
...planExists && !planAlreadyDone ? ["- Set non-completed phases/tasks to closed status"] : []
|
|
45104
|
+
...planExists && !planAlreadyDone ? ["- Set non-completed phases/tasks to closed status"] : [],
|
|
45105
|
+
...gitAlignResult ? [`- Git: ${gitAlignResult}`] : []
|
|
44860
45106
|
];
|
|
44861
45107
|
const summaryContent = [
|
|
44862
45108
|
"# Swarm Close Summary",
|
|
44863
45109
|
"",
|
|
44864
45110
|
`**Project:** ${projectName}`,
|
|
44865
45111
|
`**Closed:** ${new Date().toISOString()}`,
|
|
45112
|
+
`**Finalization:** ${finalizationType}`,
|
|
44866
45113
|
"",
|
|
44867
45114
|
`## Phases Closed: ${closedPhases.length}`,
|
|
44868
45115
|
!planExists ? "_No plan \u2014 ad-hoc session_" : closedPhases.length > 0 ? closedPhases.map((id) => `- Phase ${id}`).join(`
|
|
@@ -44873,7 +45120,9 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44873
45120
|
`) : "_No incomplete tasks_",
|
|
44874
45121
|
"",
|
|
44875
45122
|
"## Actions Performed",
|
|
44876
|
-
...actionsPerformed
|
|
45123
|
+
...actionsPerformed,
|
|
45124
|
+
"",
|
|
45125
|
+
...warnings.length > 0 ? ["## Warnings", ...warnings.map((w) => `- ${w}`), ""] : []
|
|
44877
45126
|
].join(`
|
|
44878
45127
|
`);
|
|
44879
45128
|
try {
|
|
@@ -44892,11 +45141,21 @@ async function handleCloseCommand(directory, args2) {
|
|
|
44892
45141
|
if (pruneErrors.length > 0) {
|
|
44893
45142
|
warnings.push(`Could not prune ${pruneErrors.length} branch(es) (unmerged or checked out): ${pruneErrors.join(", ")}`);
|
|
44894
45143
|
}
|
|
44895
|
-
const warningMsg = warnings.length > 0 ? `
|
|
45144
|
+
const warningMsg = warnings.length > 0 ? `
|
|
45145
|
+
|
|
45146
|
+
**Warnings:**
|
|
45147
|
+
${warnings.map((w) => `- ${w}`).join(`
|
|
45148
|
+
`)}` : "";
|
|
44896
45149
|
if (planAlreadyDone) {
|
|
44897
|
-
return `\u2705 Session
|
|
45150
|
+
return `\u2705 Session finalized. Plan was already in a terminal state \u2014 cleanup and archive applied.
|
|
45151
|
+
|
|
45152
|
+
**Archive:** ${archiveResult}
|
|
45153
|
+
**Git:** ${gitAlignResult}${warningMsg}`;
|
|
44898
45154
|
}
|
|
44899
|
-
return `\u2705 Swarm
|
|
45155
|
+
return `\u2705 Swarm finalized. ${closedPhases.length} phase(s) closed, ${closedTasks.length} incomplete task(s) marked closed.
|
|
45156
|
+
|
|
45157
|
+
**Archive:** ${archiveResult}
|
|
45158
|
+
**Git:** ${gitAlignResult}${warningMsg}`;
|
|
44900
45159
|
}
|
|
44901
45160
|
|
|
44902
45161
|
// src/commands/config.ts
|
|
@@ -46051,7 +46310,7 @@ init_loader();
|
|
|
46051
46310
|
init_manager();
|
|
46052
46311
|
init_utils2();
|
|
46053
46312
|
init_manager2();
|
|
46054
|
-
import * as
|
|
46313
|
+
import * as child_process4 from "child_process";
|
|
46055
46314
|
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
46056
46315
|
import path19 from "path";
|
|
46057
46316
|
import { fileURLToPath } from "url";
|
|
@@ -46293,7 +46552,7 @@ async function checkGitRepository(directory) {
|
|
|
46293
46552
|
detail: "Invalid directory \u2014 cannot check git status"
|
|
46294
46553
|
};
|
|
46295
46554
|
}
|
|
46296
|
-
|
|
46555
|
+
child_process4.execSync("git rev-parse --git-dir", {
|
|
46297
46556
|
cwd: directory,
|
|
46298
46557
|
stdio: "pipe"
|
|
46299
46558
|
});
|
|
@@ -47177,7 +47436,7 @@ async function handleExportCommand(directory, _args) {
|
|
|
47177
47436
|
init_state();
|
|
47178
47437
|
async function handleFullAutoCommand(_directory, args2, sessionID) {
|
|
47179
47438
|
if (!sessionID || sessionID.trim() === "") {
|
|
47180
|
-
return "Error: No active session context. Full-Auto Mode requires an active session. Use /swarm
|
|
47439
|
+
return "Error: No active session context. Full-Auto Mode requires an active session. Use /swarm-full-auto from within an OpenCode session, or start a session first.";
|
|
47181
47440
|
}
|
|
47182
47441
|
const session = getAgentSession(sessionID);
|
|
47183
47442
|
if (!session) {
|
|
@@ -47185,16 +47444,15 @@ async function handleFullAutoCommand(_directory, args2, sessionID) {
|
|
|
47185
47444
|
}
|
|
47186
47445
|
const arg = args2[0]?.toLowerCase();
|
|
47187
47446
|
let newFullAutoMode;
|
|
47188
|
-
let feedback;
|
|
47189
47447
|
if (arg === "on") {
|
|
47190
47448
|
newFullAutoMode = true;
|
|
47191
|
-
feedback = "Full-Auto Mode enabled";
|
|
47192
47449
|
} else if (arg === "off") {
|
|
47193
47450
|
newFullAutoMode = false;
|
|
47194
|
-
feedback = "Full-Auto Mode disabled";
|
|
47195
47451
|
} else {
|
|
47196
47452
|
newFullAutoMode = !session.fullAutoMode;
|
|
47197
|
-
|
|
47453
|
+
}
|
|
47454
|
+
if (newFullAutoMode && !swarmState.fullAutoEnabledInConfig) {
|
|
47455
|
+
return "Error: Full-Auto Mode cannot be enabled because full_auto.enabled is not set to true in the swarm plugin config. The autonomous oversight hook is inactive without config-level enablement. Set full_auto.enabled = true in your opencode-swarm config and restart.";
|
|
47198
47456
|
}
|
|
47199
47457
|
session.fullAutoMode = newFullAutoMode;
|
|
47200
47458
|
if (!newFullAutoMode) {
|
|
@@ -47202,7 +47460,7 @@ async function handleFullAutoCommand(_directory, args2, sessionID) {
|
|
|
47202
47460
|
session.fullAutoDeadlockCount = 0;
|
|
47203
47461
|
session.fullAutoLastQuestionHash = null;
|
|
47204
47462
|
}
|
|
47205
|
-
return
|
|
47463
|
+
return newFullAutoMode ? "Full-Auto Mode enabled" : "Full-Auto Mode disabled";
|
|
47206
47464
|
}
|
|
47207
47465
|
|
|
47208
47466
|
// src/commands/handoff.ts
|
|
@@ -47510,6 +47768,64 @@ function formatHandoffMarkdown(data) {
|
|
|
47510
47768
|
return lines.join(`
|
|
47511
47769
|
`);
|
|
47512
47770
|
}
|
|
47771
|
+
function formatContinuationPrompt(data) {
|
|
47772
|
+
const lines = [];
|
|
47773
|
+
lines.push("## Resume Swarm");
|
|
47774
|
+
lines.push("");
|
|
47775
|
+
if (data.currentPhase) {
|
|
47776
|
+
lines.push(`**Phase**: ${data.currentPhase}`);
|
|
47777
|
+
}
|
|
47778
|
+
if (data.currentTask) {
|
|
47779
|
+
lines.push(`**Current Task**: ${data.currentTask}`);
|
|
47780
|
+
}
|
|
47781
|
+
let nextTask;
|
|
47782
|
+
if (data.incompleteTasks.length > 0) {
|
|
47783
|
+
nextTask = data.incompleteTasks.find((t) => t !== data.currentTask);
|
|
47784
|
+
if (nextTask) {
|
|
47785
|
+
lines.push(`**Next Task**: ${nextTask}`);
|
|
47786
|
+
}
|
|
47787
|
+
}
|
|
47788
|
+
if (data.pendingQA) {
|
|
47789
|
+
lines.push("");
|
|
47790
|
+
lines.push(`**Pending QA Blocker**: ${data.pendingQA.taskId}`);
|
|
47791
|
+
if (data.pendingQA.lastFailure) {
|
|
47792
|
+
lines.push(` - Last failure: ${data.pendingQA.lastFailure}`);
|
|
47793
|
+
}
|
|
47794
|
+
}
|
|
47795
|
+
if (data.recentDecisions.length > 0) {
|
|
47796
|
+
const last3 = data.recentDecisions.slice(-3);
|
|
47797
|
+
lines.push("");
|
|
47798
|
+
lines.push("**Recent Decisions (do not revisit)**:");
|
|
47799
|
+
for (const decision of last3) {
|
|
47800
|
+
lines.push(`- ${decision}`);
|
|
47801
|
+
}
|
|
47802
|
+
}
|
|
47803
|
+
if (data.incompleteTasks.length > 2) {
|
|
47804
|
+
const remaining = data.incompleteTasks.filter((t) => t !== data.currentTask && t !== nextTask);
|
|
47805
|
+
if (remaining.length > 0) {
|
|
47806
|
+
lines.push("");
|
|
47807
|
+
lines.push(`**Remaining Tasks**: ${remaining.slice(0, 8).join(", ")}${remaining.length > 8 ? ` (+${remaining.length - 8} more)` : ""}`);
|
|
47808
|
+
}
|
|
47809
|
+
}
|
|
47810
|
+
lines.push("");
|
|
47811
|
+
lines.push("**To resume**:");
|
|
47812
|
+
lines.push("1. Read `.swarm/handoff.md` for full context");
|
|
47813
|
+
lines.push("2. Use `knowledge_recall` to recall relevant lessons before starting");
|
|
47814
|
+
if (data.pendingQA) {
|
|
47815
|
+
lines.push(`3. Resolve QA blocker on task ${data.pendingQA.taskId} before continuing`);
|
|
47816
|
+
} else if (data.currentTask) {
|
|
47817
|
+
lines.push(`3. Continue work on task ${data.currentTask}`);
|
|
47818
|
+
} else if (nextTask) {
|
|
47819
|
+
lines.push(`3. Begin work on task ${nextTask}`);
|
|
47820
|
+
} else {
|
|
47821
|
+
lines.push("3. Review the plan and pick up the next incomplete task");
|
|
47822
|
+
}
|
|
47823
|
+
lines.push("4. Do not re-implement completed tasks or revisit settled decisions");
|
|
47824
|
+
return `\`\`\`markdown
|
|
47825
|
+
${lines.join(`
|
|
47826
|
+
`)}
|
|
47827
|
+
\`\`\``;
|
|
47828
|
+
}
|
|
47513
47829
|
|
|
47514
47830
|
// src/commands/handoff.ts
|
|
47515
47831
|
init_state();
|
|
@@ -47520,17 +47836,27 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
47520
47836
|
const tempPath = `${resolvedPath}.tmp.${crypto4.randomUUID()}`;
|
|
47521
47837
|
await Bun.write(tempPath, markdown);
|
|
47522
47838
|
renameSync7(tempPath, resolvedPath);
|
|
47839
|
+
const continuationPrompt = formatContinuationPrompt(handoffData);
|
|
47840
|
+
const promptPath = validateSwarmPath(directory, "handoff-prompt.md");
|
|
47841
|
+
const promptTempPath = `${promptPath}.tmp.${crypto4.randomUUID()}`;
|
|
47842
|
+
await Bun.write(promptTempPath, continuationPrompt);
|
|
47843
|
+
renameSync7(promptTempPath, promptPath);
|
|
47523
47844
|
await writeSnapshot(directory, swarmState);
|
|
47524
47845
|
await flushPendingSnapshot(directory);
|
|
47525
47846
|
return `## Handoff Brief Written
|
|
47526
47847
|
|
|
47527
47848
|
Brief written to \`.swarm/handoff.md\`.
|
|
47849
|
+
Continuation prompt written to \`.swarm/handoff-prompt.md\`.
|
|
47528
47850
|
|
|
47529
47851
|
${markdown}
|
|
47530
47852
|
|
|
47531
47853
|
---
|
|
47532
47854
|
|
|
47533
|
-
|
|
47855
|
+
## Continuation Prompt
|
|
47856
|
+
|
|
47857
|
+
Copy and paste the block below into your next session to resume cleanly:
|
|
47858
|
+
|
|
47859
|
+
${continuationPrompt}`;
|
|
47534
47860
|
}
|
|
47535
47861
|
|
|
47536
47862
|
// src/services/history-service.ts
|
|
@@ -50089,6 +50415,11 @@ var COMMAND_REGISTRY = {
|
|
|
50089
50415
|
description: "Run config doctor checks",
|
|
50090
50416
|
subcommandOf: "config"
|
|
50091
50417
|
},
|
|
50418
|
+
"config-doctor": {
|
|
50419
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
50420
|
+
description: "Run config doctor checks",
|
|
50421
|
+
subcommandOf: "config"
|
|
50422
|
+
},
|
|
50092
50423
|
"doctor tools": {
|
|
50093
50424
|
handler: (ctx) => handleDoctorToolsCommand(ctx.directory, ctx.args),
|
|
50094
50425
|
description: "Run tool registration coherence check"
|
|
@@ -50122,6 +50453,11 @@ var COMMAND_REGISTRY = {
|
|
|
50122
50453
|
description: "Generate evidence summary with completion ratio and blockers",
|
|
50123
50454
|
subcommandOf: "evidence"
|
|
50124
50455
|
},
|
|
50456
|
+
"evidence-summary": {
|
|
50457
|
+
handler: (ctx) => handleEvidenceSummaryCommand(ctx.directory),
|
|
50458
|
+
description: "Generate evidence summary with completion ratio and blockers",
|
|
50459
|
+
subcommandOf: "evidence"
|
|
50460
|
+
},
|
|
50125
50461
|
archive: {
|
|
50126
50462
|
handler: (ctx) => handleArchiveCommand(ctx.directory, ctx.args),
|
|
50127
50463
|
description: "Archive old evidence bundles [--dry-run]"
|
|
@@ -56151,12 +56487,23 @@ init_state();
|
|
|
56151
56487
|
init_telemetry();
|
|
56152
56488
|
init_utils2();
|
|
56153
56489
|
var END_OF_SENTENCE_QUESTION_PATTERN = /\?\s*$/;
|
|
56154
|
-
var
|
|
56155
|
-
/Ready for Phase (?:\d+|\[?N\+1\]?)
|
|
56490
|
+
var PHASE_COMPLETION_PATTERNS = [
|
|
56491
|
+
/Ready for Phase (?:\d+|\[?N\+1\]?)\??/i,
|
|
56492
|
+
/phase.{0,20}(?:complete|finish|done|wrap)/i,
|
|
56493
|
+
/move(?:d?)?\s+(?:on\s+)?to\s+(?:the\s+)?(?:next\s+)?phase/i
|
|
56494
|
+
];
|
|
56495
|
+
var QUESTION_ESCALATION_PATTERNS = [
|
|
56156
56496
|
/escalat/i,
|
|
56157
56497
|
/What would you like/i,
|
|
56158
56498
|
/Should I proceed/i,
|
|
56159
|
-
/Do you want/i
|
|
56499
|
+
/Do you want/i,
|
|
56500
|
+
/Shall I/i,
|
|
56501
|
+
/Would you like/i,
|
|
56502
|
+
/Can I proceed/i,
|
|
56503
|
+
/May I proceed/i,
|
|
56504
|
+
/Awaiting (?:your |)(?:approval|confirmation|input|decision|direction)/i,
|
|
56505
|
+
/Please (?:confirm|approve|advise|let me know)/i,
|
|
56506
|
+
/How (?:would you like|should I)/i
|
|
56160
56507
|
];
|
|
56161
56508
|
var MID_SENTENCE_QUESTION_PATTERNS = [
|
|
56162
56509
|
/\b(v\d+\?)/i,
|
|
@@ -56193,11 +56540,16 @@ function resolveOversightAgentName(architectAgentName) {
|
|
|
56193
56540
|
return `${prefix}critic_oversight`;
|
|
56194
56541
|
}
|
|
56195
56542
|
function detectEscalation(text) {
|
|
56196
|
-
for (const pattern of
|
|
56543
|
+
for (const pattern of PHASE_COMPLETION_PATTERNS) {
|
|
56197
56544
|
if (pattern.test(text)) {
|
|
56198
56545
|
return "phase_completion";
|
|
56199
56546
|
}
|
|
56200
56547
|
}
|
|
56548
|
+
for (const pattern of QUESTION_ESCALATION_PATTERNS) {
|
|
56549
|
+
if (pattern.test(text)) {
|
|
56550
|
+
return "question";
|
|
56551
|
+
}
|
|
56552
|
+
}
|
|
56201
56553
|
if (END_OF_SENTENCE_QUESTION_PATTERN.test(text)) {
|
|
56202
56554
|
if (!isMidSentenceQuestion(text)) {
|
|
56203
56555
|
return "question";
|
|
@@ -56339,7 +56691,7 @@ async function writeAutoOversightEvent(directory, architectOutput, criticVerdict
|
|
|
56339
56691
|
}
|
|
56340
56692
|
}
|
|
56341
56693
|
}
|
|
56342
|
-
function injectVerdictIntoMessages(messages, architectIndex, criticResult,
|
|
56694
|
+
function injectVerdictIntoMessages(messages, architectIndex, criticResult, escalationType, oversightAgentName) {
|
|
56343
56695
|
if (criticResult.escalationNeeded || criticResult.verdict === "ESCALATE_TO_HUMAN") {
|
|
56344
56696
|
const verdictMessage2 = {
|
|
56345
56697
|
info: {
|
|
@@ -56376,6 +56728,19 @@ ${criticResult.reasoning}`
|
|
|
56376
56728
|
]
|
|
56377
56729
|
};
|
|
56378
56730
|
messages.splice(architectIndex + 1, 0, verdictMessage2);
|
|
56731
|
+
const continuationMessage = {
|
|
56732
|
+
info: {
|
|
56733
|
+
role: "user",
|
|
56734
|
+
agent: oversightAgentName
|
|
56735
|
+
},
|
|
56736
|
+
parts: [
|
|
56737
|
+
{
|
|
56738
|
+
type: "text",
|
|
56739
|
+
text: "[FULL-AUTO CONTINUATION] The critic has answered your question. Incorporate the answer above and continue executing the current plan. Do not ask follow-up questions about this answer \u2014 proceed with implementation."
|
|
56740
|
+
}
|
|
56741
|
+
]
|
|
56742
|
+
};
|
|
56743
|
+
messages.splice(architectIndex + 2, 0, continuationMessage);
|
|
56379
56744
|
return;
|
|
56380
56745
|
}
|
|
56381
56746
|
const verdictEmoji = criticResult.verdict === "APPROVED" ? "\u2705" : criticResult.verdict === "NEEDS_REVISION" ? "\uD83D\uDD04" : criticResult.verdict === "REJECTED" ? "\u274C" : criticResult.verdict === "BLOCKED" ? "\uD83D\uDEAB" : "\uD83D\uDCAC";
|
|
@@ -56394,6 +56759,35 @@ Critic reasoning: ${criticResult.reasoning}`
|
|
|
56394
56759
|
]
|
|
56395
56760
|
};
|
|
56396
56761
|
messages.splice(architectIndex + 1, 0, verdictMessage);
|
|
56762
|
+
if (criticResult.verdict === "APPROVED" && escalationType === "phase_completion") {
|
|
56763
|
+
const continuationMessage = {
|
|
56764
|
+
info: {
|
|
56765
|
+
role: "user",
|
|
56766
|
+
agent: oversightAgentName
|
|
56767
|
+
},
|
|
56768
|
+
parts: [
|
|
56769
|
+
{
|
|
56770
|
+
type: "text",
|
|
56771
|
+
text: "[FULL-AUTO CONTINUATION] Phase approved by autonomous oversight. Call `phase_complete` now to finalize this phase, then proceed to the next phase in the plan. Do not wait for further human input."
|
|
56772
|
+
}
|
|
56773
|
+
]
|
|
56774
|
+
};
|
|
56775
|
+
messages.splice(architectIndex + 2, 0, continuationMessage);
|
|
56776
|
+
} else if (criticResult.verdict === "APPROVED") {
|
|
56777
|
+
const continuationMessage = {
|
|
56778
|
+
info: {
|
|
56779
|
+
role: "user",
|
|
56780
|
+
agent: oversightAgentName
|
|
56781
|
+
},
|
|
56782
|
+
parts: [
|
|
56783
|
+
{
|
|
56784
|
+
type: "text",
|
|
56785
|
+
text: "[FULL-AUTO CONTINUATION] Approved by autonomous oversight. Continue executing the current task and plan. Do not wait for further human input."
|
|
56786
|
+
}
|
|
56787
|
+
]
|
|
56788
|
+
};
|
|
56789
|
+
messages.splice(architectIndex + 2, 0, continuationMessage);
|
|
56790
|
+
}
|
|
56397
56791
|
}
|
|
56398
56792
|
async function dispatchCriticAndWriteEvent(directory, architectOutput, criticContext, criticModel, escalationType, interactionCount, deadlockCount, oversightAgentName) {
|
|
56399
56793
|
const client = swarmState.opencodeClient;
|
|
@@ -57879,6 +58273,10 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
57879
58273
|
fs35.unlinkSync(consumedPath);
|
|
57880
58274
|
}
|
|
57881
58275
|
fs35.renameSync(handoffPath, consumedPath);
|
|
58276
|
+
try {
|
|
58277
|
+
const promptPath = validateSwarmPath(directory, "handoff-prompt.md");
|
|
58278
|
+
fs35.unlinkSync(promptPath);
|
|
58279
|
+
} catch {}
|
|
57882
58280
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
57883
58281
|
The previous model's session ended. Here is your starting context:
|
|
57884
58282
|
|
|
@@ -58992,14 +59390,14 @@ import * as fs36 from "fs";
|
|
|
58992
59390
|
import * as path47 from "path";
|
|
58993
59391
|
|
|
58994
59392
|
// src/hooks/spawn-helper.ts
|
|
58995
|
-
import * as
|
|
59393
|
+
import * as child_process5 from "child_process";
|
|
58996
59394
|
var WIN32_CMD_BINARIES = new Set(["npm", "npx", "pnpm", "yarn"]);
|
|
58997
59395
|
function spawnAsync(command, cwd, timeoutMs) {
|
|
58998
59396
|
return new Promise((resolve15) => {
|
|
58999
59397
|
try {
|
|
59000
59398
|
const [rawCmd, ...args2] = command;
|
|
59001
59399
|
const cmd = process.platform === "win32" && WIN32_CMD_BINARIES.has(rawCmd) && !rawCmd.includes(".") ? `${rawCmd}.cmd` : rawCmd;
|
|
59002
|
-
const proc =
|
|
59400
|
+
const proc = child_process5.spawn(cmd, args2, {
|
|
59003
59401
|
cwd,
|
|
59004
59402
|
stdio: ["ignore", "pipe", "pipe"]
|
|
59005
59403
|
});
|
|
@@ -60007,6 +60405,9 @@ async function rehydrateState(snapshot) {
|
|
|
60007
60405
|
for (const field of TRANSIENT_SESSION_FIELDS) {
|
|
60008
60406
|
session[field.name] = field.resetValue;
|
|
60009
60407
|
}
|
|
60408
|
+
if (session.fullAutoMode && !swarmState.fullAutoEnabledInConfig) {
|
|
60409
|
+
session.fullAutoMode = false;
|
|
60410
|
+
}
|
|
60010
60411
|
swarmState.agentSessions.set(sessionId, session);
|
|
60011
60412
|
}
|
|
60012
60413
|
}
|
|
@@ -62408,7 +62809,7 @@ var declare_scope = createSwarmTool({
|
|
|
62408
62809
|
});
|
|
62409
62810
|
// src/tools/diff.ts
|
|
62410
62811
|
init_dist();
|
|
62411
|
-
import * as
|
|
62812
|
+
import * as child_process6 from "child_process";
|
|
62412
62813
|
|
|
62413
62814
|
// src/diff/ast-diff.ts
|
|
62414
62815
|
init_tree_sitter();
|
|
@@ -62776,13 +63177,13 @@ var diff = createSwarmTool({
|
|
|
62776
63177
|
numstatArgs.push("--", ...typedArgs.paths);
|
|
62777
63178
|
fullDiffArgs.push("--", ...typedArgs.paths);
|
|
62778
63179
|
}
|
|
62779
|
-
const numstatOutput =
|
|
63180
|
+
const numstatOutput = child_process6.execFileSync("git", numstatArgs, {
|
|
62780
63181
|
encoding: "utf-8",
|
|
62781
63182
|
timeout: DIFF_TIMEOUT_MS,
|
|
62782
63183
|
maxBuffer: MAX_BUFFER_BYTES,
|
|
62783
63184
|
cwd: directory
|
|
62784
63185
|
});
|
|
62785
|
-
const fullDiffOutput =
|
|
63186
|
+
const fullDiffOutput = child_process6.execFileSync("git", fullDiffArgs, {
|
|
62786
63187
|
encoding: "utf-8",
|
|
62787
63188
|
timeout: DIFF_TIMEOUT_MS,
|
|
62788
63189
|
maxBuffer: MAX_BUFFER_BYTES,
|
|
@@ -62831,23 +63232,23 @@ var diff = createSwarmTool({
|
|
|
62831
63232
|
let oldContent;
|
|
62832
63233
|
let newContent;
|
|
62833
63234
|
if (base === "staged") {
|
|
62834
|
-
oldContent =
|
|
63235
|
+
oldContent = child_process6.execFileSync("git", ["show", `HEAD:${file3.path}`], {
|
|
62835
63236
|
encoding: "utf-8",
|
|
62836
63237
|
timeout: 5000,
|
|
62837
63238
|
cwd: directory
|
|
62838
63239
|
});
|
|
62839
|
-
newContent =
|
|
63240
|
+
newContent = child_process6.execFileSync("git", ["show", `:${file3.path}`], {
|
|
62840
63241
|
encoding: "utf-8",
|
|
62841
63242
|
timeout: 5000,
|
|
62842
63243
|
cwd: directory
|
|
62843
63244
|
});
|
|
62844
63245
|
} else if (base === "unstaged") {
|
|
62845
|
-
oldContent =
|
|
63246
|
+
oldContent = child_process6.execFileSync("git", ["show", `:${file3.path}`], {
|
|
62846
63247
|
encoding: "utf-8",
|
|
62847
63248
|
timeout: 5000,
|
|
62848
63249
|
cwd: directory
|
|
62849
63250
|
});
|
|
62850
|
-
newContent =
|
|
63251
|
+
newContent = child_process6.execFileSync("git", ["show", `HEAD:${file3.path}`], {
|
|
62851
63252
|
encoding: "utf-8",
|
|
62852
63253
|
timeout: 5000,
|
|
62853
63254
|
cwd: directory
|
|
@@ -62856,12 +63257,12 @@ var diff = createSwarmTool({
|
|
|
62856
63257
|
const pathModule = await import("path");
|
|
62857
63258
|
newContent = fsModule.readFileSync(pathModule.join(directory, file3.path), "utf-8");
|
|
62858
63259
|
} else {
|
|
62859
|
-
oldContent =
|
|
63260
|
+
oldContent = child_process6.execFileSync("git", ["show", `${base}:${file3.path}`], {
|
|
62860
63261
|
encoding: "utf-8",
|
|
62861
63262
|
timeout: 5000,
|
|
62862
63263
|
cwd: directory
|
|
62863
63264
|
});
|
|
62864
|
-
newContent =
|
|
63265
|
+
newContent = child_process6.execFileSync("git", ["show", `HEAD:${file3.path}`], {
|
|
62865
63266
|
encoding: "utf-8",
|
|
62866
63267
|
timeout: 5000,
|
|
62867
63268
|
cwd: directory
|
|
@@ -64972,7 +65373,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
64972
65373
|
}
|
|
64973
65374
|
} catch {}
|
|
64974
65375
|
}
|
|
64975
|
-
} catch (
|
|
65376
|
+
} catch (_error) {
|
|
64976
65377
|
if (await ledgerExists(dir)) {
|
|
64977
65378
|
try {
|
|
64978
65379
|
const rebuilt = await replayFromLedger(dir);
|
|
@@ -67685,7 +68086,7 @@ function executeRulesSync(filePath, content, language) {
|
|
|
67685
68086
|
}
|
|
67686
68087
|
|
|
67687
68088
|
// src/sast/semgrep.ts
|
|
67688
|
-
import * as
|
|
68089
|
+
import * as child_process7 from "child_process";
|
|
67689
68090
|
var semgrepAvailableCache = null;
|
|
67690
68091
|
var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
|
|
67691
68092
|
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
@@ -67694,7 +68095,7 @@ function isSemgrepAvailable() {
|
|
|
67694
68095
|
return semgrepAvailableCache;
|
|
67695
68096
|
}
|
|
67696
68097
|
try {
|
|
67697
|
-
|
|
68098
|
+
child_process7.execFileSync("semgrep", ["--version"], {
|
|
67698
68099
|
encoding: "utf-8",
|
|
67699
68100
|
stdio: "pipe"
|
|
67700
68101
|
});
|
|
@@ -67753,7 +68154,7 @@ function mapSemgrepSeverity(severity) {
|
|
|
67753
68154
|
}
|
|
67754
68155
|
async function executeWithTimeout(command, args2, options) {
|
|
67755
68156
|
return new Promise((resolve24) => {
|
|
67756
|
-
const child =
|
|
68157
|
+
const child = child_process7.spawn(command, args2, {
|
|
67757
68158
|
shell: false,
|
|
67758
68159
|
cwd: options.cwd
|
|
67759
68160
|
});
|
|
@@ -72699,7 +73100,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
72699
73100
|
...opencodeConfig.command || {},
|
|
72700
73101
|
swarm: {
|
|
72701
73102
|
template: "/swarm $ARGUMENTS",
|
|
72702
|
-
description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|dark-matter|knowledge|curate|close]"
|
|
73103
|
+
description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|dark-matter|knowledge|curate|turbo|full-auto|write-retro|reset-session|simulate|promote|checkpoint|close]"
|
|
72703
73104
|
},
|
|
72704
73105
|
"swarm-status": {
|
|
72705
73106
|
template: "/swarm status",
|
|
@@ -72794,8 +73195,8 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
72794
73195
|
description: "Use /swarm turbo to enable turbo mode for faster execution"
|
|
72795
73196
|
},
|
|
72796
73197
|
"swarm-full-auto": {
|
|
72797
|
-
template: "/swarm
|
|
72798
|
-
description: "
|
|
73198
|
+
template: "/swarm-full-auto $ARGUMENTS",
|
|
73199
|
+
description: "Toggle Full-Auto Mode for the active session [on|off]"
|
|
72799
73200
|
},
|
|
72800
73201
|
"swarm-write-retro": {
|
|
72801
73202
|
template: "/swarm write-retro $ARGUMENTS",
|