opencode-swarm 6.53.3 → 6.53.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +376 -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/hooks/full-auto-intercept.d.ts +1 -1
- package/dist/index.js +470 -75
- 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,17 +31647,60 @@ 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
|
+
}
|
|
31685
|
+
}
|
|
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 {}
|
|
31660
31698
|
}
|
|
31699
|
+
} else if (!initSucceeded && backupExists) {
|
|
31700
|
+
try {
|
|
31701
|
+
if (existsSync7(oldLedgerBackupPath))
|
|
31702
|
+
unlinkSync(oldLedgerBackupPath);
|
|
31703
|
+
} catch {}
|
|
31661
31704
|
}
|
|
31662
31705
|
}
|
|
31663
31706
|
}
|
|
@@ -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
|
|
@@ -45664,15 +45923,19 @@ ${phaseDigest.summary}`,
|
|
|
45664
45923
|
async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledgeConfig) {
|
|
45665
45924
|
let applied = 0;
|
|
45666
45925
|
let skipped = 0;
|
|
45667
|
-
if (recommendations.length === 0) {
|
|
45926
|
+
if (!recommendations || recommendations.length === 0) {
|
|
45668
45927
|
return { applied, skipped };
|
|
45669
45928
|
}
|
|
45929
|
+
if (knowledgeConfig == null) {
|
|
45930
|
+
return { applied: 0, skipped: 0 };
|
|
45931
|
+
}
|
|
45932
|
+
const validRecommendations = recommendations.filter((rec) => rec != null);
|
|
45670
45933
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
45671
45934
|
const entries = await readKnowledge(knowledgePath);
|
|
45672
45935
|
let modified = false;
|
|
45673
45936
|
const appliedIds = new Set;
|
|
45674
45937
|
const updatedEntries = entries.map((entry) => {
|
|
45675
|
-
const rec =
|
|
45938
|
+
const rec = validRecommendations.find((r) => r.entry_id === entry.id);
|
|
45676
45939
|
if (!rec)
|
|
45677
45940
|
return entry;
|
|
45678
45941
|
switch (rec.action) {
|
|
@@ -45726,7 +45989,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
45726
45989
|
return entry;
|
|
45727
45990
|
}
|
|
45728
45991
|
});
|
|
45729
|
-
for (const rec of
|
|
45992
|
+
for (const rec of validRecommendations) {
|
|
45730
45993
|
if (rec.entry_id !== undefined && !appliedIds.has(rec.entry_id)) {
|
|
45731
45994
|
const found = entries.some((e) => e.id === rec.entry_id);
|
|
45732
45995
|
if (!found) {
|
|
@@ -45739,7 +46002,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
45739
46002
|
await rewriteKnowledge(knowledgePath, updatedEntries);
|
|
45740
46003
|
}
|
|
45741
46004
|
const existingLessons = entries.map((e) => e.lesson);
|
|
45742
|
-
for (const rec of
|
|
46005
|
+
for (const rec of validRecommendations) {
|
|
45743
46006
|
if (rec.entry_id !== undefined)
|
|
45744
46007
|
continue;
|
|
45745
46008
|
if (rec.action !== "promote") {
|
|
@@ -46051,7 +46314,7 @@ init_loader();
|
|
|
46051
46314
|
init_manager();
|
|
46052
46315
|
init_utils2();
|
|
46053
46316
|
init_manager2();
|
|
46054
|
-
import * as
|
|
46317
|
+
import * as child_process4 from "child_process";
|
|
46055
46318
|
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
46056
46319
|
import path19 from "path";
|
|
46057
46320
|
import { fileURLToPath } from "url";
|
|
@@ -46293,7 +46556,7 @@ async function checkGitRepository(directory) {
|
|
|
46293
46556
|
detail: "Invalid directory \u2014 cannot check git status"
|
|
46294
46557
|
};
|
|
46295
46558
|
}
|
|
46296
|
-
|
|
46559
|
+
child_process4.execSync("git rev-parse --git-dir", {
|
|
46297
46560
|
cwd: directory,
|
|
46298
46561
|
stdio: "pipe"
|
|
46299
46562
|
});
|
|
@@ -47177,7 +47440,7 @@ async function handleExportCommand(directory, _args) {
|
|
|
47177
47440
|
init_state();
|
|
47178
47441
|
async function handleFullAutoCommand(_directory, args2, sessionID) {
|
|
47179
47442
|
if (!sessionID || sessionID.trim() === "") {
|
|
47180
|
-
return "Error: No active session context. Full-Auto Mode requires an active session. Use /swarm
|
|
47443
|
+
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
47444
|
}
|
|
47182
47445
|
const session = getAgentSession(sessionID);
|
|
47183
47446
|
if (!session) {
|
|
@@ -47185,16 +47448,15 @@ async function handleFullAutoCommand(_directory, args2, sessionID) {
|
|
|
47185
47448
|
}
|
|
47186
47449
|
const arg = args2[0]?.toLowerCase();
|
|
47187
47450
|
let newFullAutoMode;
|
|
47188
|
-
let feedback;
|
|
47189
47451
|
if (arg === "on") {
|
|
47190
47452
|
newFullAutoMode = true;
|
|
47191
|
-
feedback = "Full-Auto Mode enabled";
|
|
47192
47453
|
} else if (arg === "off") {
|
|
47193
47454
|
newFullAutoMode = false;
|
|
47194
|
-
feedback = "Full-Auto Mode disabled";
|
|
47195
47455
|
} else {
|
|
47196
47456
|
newFullAutoMode = !session.fullAutoMode;
|
|
47197
|
-
|
|
47457
|
+
}
|
|
47458
|
+
if (newFullAutoMode && !swarmState.fullAutoEnabledInConfig) {
|
|
47459
|
+
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
47460
|
}
|
|
47199
47461
|
session.fullAutoMode = newFullAutoMode;
|
|
47200
47462
|
if (!newFullAutoMode) {
|
|
@@ -47202,7 +47464,7 @@ async function handleFullAutoCommand(_directory, args2, sessionID) {
|
|
|
47202
47464
|
session.fullAutoDeadlockCount = 0;
|
|
47203
47465
|
session.fullAutoLastQuestionHash = null;
|
|
47204
47466
|
}
|
|
47205
|
-
return
|
|
47467
|
+
return newFullAutoMode ? "Full-Auto Mode enabled" : "Full-Auto Mode disabled";
|
|
47206
47468
|
}
|
|
47207
47469
|
|
|
47208
47470
|
// src/commands/handoff.ts
|
|
@@ -47510,6 +47772,64 @@ function formatHandoffMarkdown(data) {
|
|
|
47510
47772
|
return lines.join(`
|
|
47511
47773
|
`);
|
|
47512
47774
|
}
|
|
47775
|
+
function formatContinuationPrompt(data) {
|
|
47776
|
+
const lines = [];
|
|
47777
|
+
lines.push("## Resume Swarm");
|
|
47778
|
+
lines.push("");
|
|
47779
|
+
if (data.currentPhase) {
|
|
47780
|
+
lines.push(`**Phase**: ${data.currentPhase}`);
|
|
47781
|
+
}
|
|
47782
|
+
if (data.currentTask) {
|
|
47783
|
+
lines.push(`**Current Task**: ${data.currentTask}`);
|
|
47784
|
+
}
|
|
47785
|
+
let nextTask;
|
|
47786
|
+
if (data.incompleteTasks.length > 0) {
|
|
47787
|
+
nextTask = data.incompleteTasks.find((t) => t !== data.currentTask);
|
|
47788
|
+
if (nextTask) {
|
|
47789
|
+
lines.push(`**Next Task**: ${nextTask}`);
|
|
47790
|
+
}
|
|
47791
|
+
}
|
|
47792
|
+
if (data.pendingQA) {
|
|
47793
|
+
lines.push("");
|
|
47794
|
+
lines.push(`**Pending QA Blocker**: ${data.pendingQA.taskId}`);
|
|
47795
|
+
if (data.pendingQA.lastFailure) {
|
|
47796
|
+
lines.push(` - Last failure: ${data.pendingQA.lastFailure}`);
|
|
47797
|
+
}
|
|
47798
|
+
}
|
|
47799
|
+
if (data.recentDecisions.length > 0) {
|
|
47800
|
+
const last3 = data.recentDecisions.slice(-3);
|
|
47801
|
+
lines.push("");
|
|
47802
|
+
lines.push("**Recent Decisions (do not revisit)**:");
|
|
47803
|
+
for (const decision of last3) {
|
|
47804
|
+
lines.push(`- ${decision}`);
|
|
47805
|
+
}
|
|
47806
|
+
}
|
|
47807
|
+
if (data.incompleteTasks.length > 2) {
|
|
47808
|
+
const remaining = data.incompleteTasks.filter((t) => t !== data.currentTask && t !== nextTask);
|
|
47809
|
+
if (remaining.length > 0) {
|
|
47810
|
+
lines.push("");
|
|
47811
|
+
lines.push(`**Remaining Tasks**: ${remaining.slice(0, 8).join(", ")}${remaining.length > 8 ? ` (+${remaining.length - 8} more)` : ""}`);
|
|
47812
|
+
}
|
|
47813
|
+
}
|
|
47814
|
+
lines.push("");
|
|
47815
|
+
lines.push("**To resume**:");
|
|
47816
|
+
lines.push("1. Read `.swarm/handoff.md` for full context");
|
|
47817
|
+
lines.push("2. Use `knowledge_recall` to recall relevant lessons before starting");
|
|
47818
|
+
if (data.pendingQA) {
|
|
47819
|
+
lines.push(`3. Resolve QA blocker on task ${data.pendingQA.taskId} before continuing`);
|
|
47820
|
+
} else if (data.currentTask) {
|
|
47821
|
+
lines.push(`3. Continue work on task ${data.currentTask}`);
|
|
47822
|
+
} else if (nextTask) {
|
|
47823
|
+
lines.push(`3. Begin work on task ${nextTask}`);
|
|
47824
|
+
} else {
|
|
47825
|
+
lines.push("3. Review the plan and pick up the next incomplete task");
|
|
47826
|
+
}
|
|
47827
|
+
lines.push("4. Do not re-implement completed tasks or revisit settled decisions");
|
|
47828
|
+
return `\`\`\`markdown
|
|
47829
|
+
${lines.join(`
|
|
47830
|
+
`)}
|
|
47831
|
+
\`\`\``;
|
|
47832
|
+
}
|
|
47513
47833
|
|
|
47514
47834
|
// src/commands/handoff.ts
|
|
47515
47835
|
init_state();
|
|
@@ -47520,17 +47840,27 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
47520
47840
|
const tempPath = `${resolvedPath}.tmp.${crypto4.randomUUID()}`;
|
|
47521
47841
|
await Bun.write(tempPath, markdown);
|
|
47522
47842
|
renameSync7(tempPath, resolvedPath);
|
|
47843
|
+
const continuationPrompt = formatContinuationPrompt(handoffData);
|
|
47844
|
+
const promptPath = validateSwarmPath(directory, "handoff-prompt.md");
|
|
47845
|
+
const promptTempPath = `${promptPath}.tmp.${crypto4.randomUUID()}`;
|
|
47846
|
+
await Bun.write(promptTempPath, continuationPrompt);
|
|
47847
|
+
renameSync7(promptTempPath, promptPath);
|
|
47523
47848
|
await writeSnapshot(directory, swarmState);
|
|
47524
47849
|
await flushPendingSnapshot(directory);
|
|
47525
47850
|
return `## Handoff Brief Written
|
|
47526
47851
|
|
|
47527
47852
|
Brief written to \`.swarm/handoff.md\`.
|
|
47853
|
+
Continuation prompt written to \`.swarm/handoff-prompt.md\`.
|
|
47528
47854
|
|
|
47529
47855
|
${markdown}
|
|
47530
47856
|
|
|
47531
47857
|
---
|
|
47532
47858
|
|
|
47533
|
-
|
|
47859
|
+
## Continuation Prompt
|
|
47860
|
+
|
|
47861
|
+
Copy and paste the block below into your next session to resume cleanly:
|
|
47862
|
+
|
|
47863
|
+
${continuationPrompt}`;
|
|
47534
47864
|
}
|
|
47535
47865
|
|
|
47536
47866
|
// src/services/history-service.ts
|
|
@@ -56161,12 +56491,23 @@ init_state();
|
|
|
56161
56491
|
init_telemetry();
|
|
56162
56492
|
init_utils2();
|
|
56163
56493
|
var END_OF_SENTENCE_QUESTION_PATTERN = /\?\s*$/;
|
|
56164
|
-
var
|
|
56165
|
-
/Ready for Phase (?:\d+|\[?N\+1\]?)
|
|
56494
|
+
var PHASE_COMPLETION_PATTERNS = [
|
|
56495
|
+
/Ready for Phase (?:\d+|\[?N\+1\]?)\??/i,
|
|
56496
|
+
/phase.{0,20}(?:complete|finish|done|wrap)/i,
|
|
56497
|
+
/move(?:d?)?\s+(?:on\s+)?to\s+(?:the\s+)?(?:next\s+)?phase/i
|
|
56498
|
+
];
|
|
56499
|
+
var QUESTION_ESCALATION_PATTERNS = [
|
|
56166
56500
|
/escalat/i,
|
|
56167
56501
|
/What would you like/i,
|
|
56168
56502
|
/Should I proceed/i,
|
|
56169
|
-
/Do you want/i
|
|
56503
|
+
/Do you want/i,
|
|
56504
|
+
/Shall I/i,
|
|
56505
|
+
/Would you like/i,
|
|
56506
|
+
/Can I proceed/i,
|
|
56507
|
+
/May I proceed/i,
|
|
56508
|
+
/Awaiting (?:your |)(?:approval|confirmation|input|decision|direction)/i,
|
|
56509
|
+
/Please (?:confirm|approve|advise|let me know)/i,
|
|
56510
|
+
/How (?:would you like|should I)/i
|
|
56170
56511
|
];
|
|
56171
56512
|
var MID_SENTENCE_QUESTION_PATTERNS = [
|
|
56172
56513
|
/\b(v\d+\?)/i,
|
|
@@ -56203,11 +56544,16 @@ function resolveOversightAgentName(architectAgentName) {
|
|
|
56203
56544
|
return `${prefix}critic_oversight`;
|
|
56204
56545
|
}
|
|
56205
56546
|
function detectEscalation(text) {
|
|
56206
|
-
for (const pattern of
|
|
56547
|
+
for (const pattern of PHASE_COMPLETION_PATTERNS) {
|
|
56207
56548
|
if (pattern.test(text)) {
|
|
56208
56549
|
return "phase_completion";
|
|
56209
56550
|
}
|
|
56210
56551
|
}
|
|
56552
|
+
for (const pattern of QUESTION_ESCALATION_PATTERNS) {
|
|
56553
|
+
if (pattern.test(text)) {
|
|
56554
|
+
return "question";
|
|
56555
|
+
}
|
|
56556
|
+
}
|
|
56211
56557
|
if (END_OF_SENTENCE_QUESTION_PATTERN.test(text)) {
|
|
56212
56558
|
if (!isMidSentenceQuestion(text)) {
|
|
56213
56559
|
return "question";
|
|
@@ -56349,7 +56695,7 @@ async function writeAutoOversightEvent(directory, architectOutput, criticVerdict
|
|
|
56349
56695
|
}
|
|
56350
56696
|
}
|
|
56351
56697
|
}
|
|
56352
|
-
function injectVerdictIntoMessages(messages, architectIndex, criticResult,
|
|
56698
|
+
function injectVerdictIntoMessages(messages, architectIndex, criticResult, escalationType, oversightAgentName) {
|
|
56353
56699
|
if (criticResult.escalationNeeded || criticResult.verdict === "ESCALATE_TO_HUMAN") {
|
|
56354
56700
|
const verdictMessage2 = {
|
|
56355
56701
|
info: {
|
|
@@ -56386,6 +56732,19 @@ ${criticResult.reasoning}`
|
|
|
56386
56732
|
]
|
|
56387
56733
|
};
|
|
56388
56734
|
messages.splice(architectIndex + 1, 0, verdictMessage2);
|
|
56735
|
+
const continuationMessage = {
|
|
56736
|
+
info: {
|
|
56737
|
+
role: "user",
|
|
56738
|
+
agent: oversightAgentName
|
|
56739
|
+
},
|
|
56740
|
+
parts: [
|
|
56741
|
+
{
|
|
56742
|
+
type: "text",
|
|
56743
|
+
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."
|
|
56744
|
+
}
|
|
56745
|
+
]
|
|
56746
|
+
};
|
|
56747
|
+
messages.splice(architectIndex + 2, 0, continuationMessage);
|
|
56389
56748
|
return;
|
|
56390
56749
|
}
|
|
56391
56750
|
const verdictEmoji = criticResult.verdict === "APPROVED" ? "\u2705" : criticResult.verdict === "NEEDS_REVISION" ? "\uD83D\uDD04" : criticResult.verdict === "REJECTED" ? "\u274C" : criticResult.verdict === "BLOCKED" ? "\uD83D\uDEAB" : "\uD83D\uDCAC";
|
|
@@ -56404,6 +56763,35 @@ Critic reasoning: ${criticResult.reasoning}`
|
|
|
56404
56763
|
]
|
|
56405
56764
|
};
|
|
56406
56765
|
messages.splice(architectIndex + 1, 0, verdictMessage);
|
|
56766
|
+
if (criticResult.verdict === "APPROVED" && escalationType === "phase_completion") {
|
|
56767
|
+
const continuationMessage = {
|
|
56768
|
+
info: {
|
|
56769
|
+
role: "user",
|
|
56770
|
+
agent: oversightAgentName
|
|
56771
|
+
},
|
|
56772
|
+
parts: [
|
|
56773
|
+
{
|
|
56774
|
+
type: "text",
|
|
56775
|
+
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."
|
|
56776
|
+
}
|
|
56777
|
+
]
|
|
56778
|
+
};
|
|
56779
|
+
messages.splice(architectIndex + 2, 0, continuationMessage);
|
|
56780
|
+
} else if (criticResult.verdict === "APPROVED") {
|
|
56781
|
+
const continuationMessage = {
|
|
56782
|
+
info: {
|
|
56783
|
+
role: "user",
|
|
56784
|
+
agent: oversightAgentName
|
|
56785
|
+
},
|
|
56786
|
+
parts: [
|
|
56787
|
+
{
|
|
56788
|
+
type: "text",
|
|
56789
|
+
text: "[FULL-AUTO CONTINUATION] Approved by autonomous oversight. Continue executing the current task and plan. Do not wait for further human input."
|
|
56790
|
+
}
|
|
56791
|
+
]
|
|
56792
|
+
};
|
|
56793
|
+
messages.splice(architectIndex + 2, 0, continuationMessage);
|
|
56794
|
+
}
|
|
56407
56795
|
}
|
|
56408
56796
|
async function dispatchCriticAndWriteEvent(directory, architectOutput, criticContext, criticModel, escalationType, interactionCount, deadlockCount, oversightAgentName) {
|
|
56409
56797
|
const client = swarmState.opencodeClient;
|
|
@@ -57889,6 +58277,10 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
57889
58277
|
fs35.unlinkSync(consumedPath);
|
|
57890
58278
|
}
|
|
57891
58279
|
fs35.renameSync(handoffPath, consumedPath);
|
|
58280
|
+
try {
|
|
58281
|
+
const promptPath = validateSwarmPath(directory, "handoff-prompt.md");
|
|
58282
|
+
fs35.unlinkSync(promptPath);
|
|
58283
|
+
} catch {}
|
|
57892
58284
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
57893
58285
|
The previous model's session ended. Here is your starting context:
|
|
57894
58286
|
|
|
@@ -59002,14 +59394,14 @@ import * as fs36 from "fs";
|
|
|
59002
59394
|
import * as path47 from "path";
|
|
59003
59395
|
|
|
59004
59396
|
// src/hooks/spawn-helper.ts
|
|
59005
|
-
import * as
|
|
59397
|
+
import * as child_process5 from "child_process";
|
|
59006
59398
|
var WIN32_CMD_BINARIES = new Set(["npm", "npx", "pnpm", "yarn"]);
|
|
59007
59399
|
function spawnAsync(command, cwd, timeoutMs) {
|
|
59008
59400
|
return new Promise((resolve15) => {
|
|
59009
59401
|
try {
|
|
59010
59402
|
const [rawCmd, ...args2] = command;
|
|
59011
59403
|
const cmd = process.platform === "win32" && WIN32_CMD_BINARIES.has(rawCmd) && !rawCmd.includes(".") ? `${rawCmd}.cmd` : rawCmd;
|
|
59012
|
-
const proc =
|
|
59404
|
+
const proc = child_process5.spawn(cmd, args2, {
|
|
59013
59405
|
cwd,
|
|
59014
59406
|
stdio: ["ignore", "pipe", "pipe"]
|
|
59015
59407
|
});
|
|
@@ -60017,6 +60409,9 @@ async function rehydrateState(snapshot) {
|
|
|
60017
60409
|
for (const field of TRANSIENT_SESSION_FIELDS) {
|
|
60018
60410
|
session[field.name] = field.resetValue;
|
|
60019
60411
|
}
|
|
60412
|
+
if (session.fullAutoMode && !swarmState.fullAutoEnabledInConfig) {
|
|
60413
|
+
session.fullAutoMode = false;
|
|
60414
|
+
}
|
|
60020
60415
|
swarmState.agentSessions.set(sessionId, session);
|
|
60021
60416
|
}
|
|
60022
60417
|
}
|
|
@@ -62418,7 +62813,7 @@ var declare_scope = createSwarmTool({
|
|
|
62418
62813
|
});
|
|
62419
62814
|
// src/tools/diff.ts
|
|
62420
62815
|
init_dist();
|
|
62421
|
-
import * as
|
|
62816
|
+
import * as child_process6 from "child_process";
|
|
62422
62817
|
|
|
62423
62818
|
// src/diff/ast-diff.ts
|
|
62424
62819
|
init_tree_sitter();
|
|
@@ -62786,13 +63181,13 @@ var diff = createSwarmTool({
|
|
|
62786
63181
|
numstatArgs.push("--", ...typedArgs.paths);
|
|
62787
63182
|
fullDiffArgs.push("--", ...typedArgs.paths);
|
|
62788
63183
|
}
|
|
62789
|
-
const numstatOutput =
|
|
63184
|
+
const numstatOutput = child_process6.execFileSync("git", numstatArgs, {
|
|
62790
63185
|
encoding: "utf-8",
|
|
62791
63186
|
timeout: DIFF_TIMEOUT_MS,
|
|
62792
63187
|
maxBuffer: MAX_BUFFER_BYTES,
|
|
62793
63188
|
cwd: directory
|
|
62794
63189
|
});
|
|
62795
|
-
const fullDiffOutput =
|
|
63190
|
+
const fullDiffOutput = child_process6.execFileSync("git", fullDiffArgs, {
|
|
62796
63191
|
encoding: "utf-8",
|
|
62797
63192
|
timeout: DIFF_TIMEOUT_MS,
|
|
62798
63193
|
maxBuffer: MAX_BUFFER_BYTES,
|
|
@@ -62841,23 +63236,23 @@ var diff = createSwarmTool({
|
|
|
62841
63236
|
let oldContent;
|
|
62842
63237
|
let newContent;
|
|
62843
63238
|
if (base === "staged") {
|
|
62844
|
-
oldContent =
|
|
63239
|
+
oldContent = child_process6.execFileSync("git", ["show", `HEAD:${file3.path}`], {
|
|
62845
63240
|
encoding: "utf-8",
|
|
62846
63241
|
timeout: 5000,
|
|
62847
63242
|
cwd: directory
|
|
62848
63243
|
});
|
|
62849
|
-
newContent =
|
|
63244
|
+
newContent = child_process6.execFileSync("git", ["show", `:${file3.path}`], {
|
|
62850
63245
|
encoding: "utf-8",
|
|
62851
63246
|
timeout: 5000,
|
|
62852
63247
|
cwd: directory
|
|
62853
63248
|
});
|
|
62854
63249
|
} else if (base === "unstaged") {
|
|
62855
|
-
oldContent =
|
|
63250
|
+
oldContent = child_process6.execFileSync("git", ["show", `:${file3.path}`], {
|
|
62856
63251
|
encoding: "utf-8",
|
|
62857
63252
|
timeout: 5000,
|
|
62858
63253
|
cwd: directory
|
|
62859
63254
|
});
|
|
62860
|
-
newContent =
|
|
63255
|
+
newContent = child_process6.execFileSync("git", ["show", `HEAD:${file3.path}`], {
|
|
62861
63256
|
encoding: "utf-8",
|
|
62862
63257
|
timeout: 5000,
|
|
62863
63258
|
cwd: directory
|
|
@@ -62866,12 +63261,12 @@ var diff = createSwarmTool({
|
|
|
62866
63261
|
const pathModule = await import("path");
|
|
62867
63262
|
newContent = fsModule.readFileSync(pathModule.join(directory, file3.path), "utf-8");
|
|
62868
63263
|
} else {
|
|
62869
|
-
oldContent =
|
|
63264
|
+
oldContent = child_process6.execFileSync("git", ["show", `${base}:${file3.path}`], {
|
|
62870
63265
|
encoding: "utf-8",
|
|
62871
63266
|
timeout: 5000,
|
|
62872
63267
|
cwd: directory
|
|
62873
63268
|
});
|
|
62874
|
-
newContent =
|
|
63269
|
+
newContent = child_process6.execFileSync("git", ["show", `HEAD:${file3.path}`], {
|
|
62875
63270
|
encoding: "utf-8",
|
|
62876
63271
|
timeout: 5000,
|
|
62877
63272
|
cwd: directory
|
|
@@ -67695,7 +68090,7 @@ function executeRulesSync(filePath, content, language) {
|
|
|
67695
68090
|
}
|
|
67696
68091
|
|
|
67697
68092
|
// src/sast/semgrep.ts
|
|
67698
|
-
import * as
|
|
68093
|
+
import * as child_process7 from "child_process";
|
|
67699
68094
|
var semgrepAvailableCache = null;
|
|
67700
68095
|
var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
|
|
67701
68096
|
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
@@ -67704,7 +68099,7 @@ function isSemgrepAvailable() {
|
|
|
67704
68099
|
return semgrepAvailableCache;
|
|
67705
68100
|
}
|
|
67706
68101
|
try {
|
|
67707
|
-
|
|
68102
|
+
child_process7.execFileSync("semgrep", ["--version"], {
|
|
67708
68103
|
encoding: "utf-8",
|
|
67709
68104
|
stdio: "pipe"
|
|
67710
68105
|
});
|
|
@@ -67763,7 +68158,7 @@ function mapSemgrepSeverity(severity) {
|
|
|
67763
68158
|
}
|
|
67764
68159
|
async function executeWithTimeout(command, args2, options) {
|
|
67765
68160
|
return new Promise((resolve24) => {
|
|
67766
|
-
const child =
|
|
68161
|
+
const child = child_process7.spawn(command, args2, {
|
|
67767
68162
|
shell: false,
|
|
67768
68163
|
cwd: options.cwd
|
|
67769
68164
|
});
|
|
@@ -72804,8 +73199,8 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
72804
73199
|
description: "Use /swarm turbo to enable turbo mode for faster execution"
|
|
72805
73200
|
},
|
|
72806
73201
|
"swarm-full-auto": {
|
|
72807
|
-
template: "/swarm
|
|
72808
|
-
description: "
|
|
73202
|
+
template: "/swarm-full-auto $ARGUMENTS",
|
|
73203
|
+
description: "Toggle Full-Auto Mode for the active session [on|off]"
|
|
72809
73204
|
},
|
|
72810
73205
|
"swarm-write-retro": {
|
|
72811
73206
|
template: "/swarm write-retro $ARGUMENTS",
|