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/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 archivePath = path11.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
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
- renameSync4(oldLedgerPath, archivePath);
31653
- warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
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
- try {
31656
- await initLedger(directory, planId, planHashForInit);
31657
- } catch (initErr) {
31658
- if (!(initErr instanceof Error && initErr.message.includes("already initialized"))) {
31659
- throw initErr;
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 child_process2 from "child_process";
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(child_process2.execFile);
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(path14.join(directory, ".swarm")).then(() => true).catch(() => false);
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: "Phase closed via /swarm close",
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(directory, ".swarm", "close-lessons.md");
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(directory, ".swarm", "context.md");
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
- if (pruneBranches) {
45007
+ let gitAlignResult = "";
45008
+ const isGit = isGitRepo2(directory);
45009
+ if (isGit) {
44827
45010
  try {
44828
- const branchOutput = execFileSync("git", ["branch", "-vv"], {
44829
- cwd: directory,
44830
- encoding: "utf-8",
44831
- stdio: ["pipe", "pipe", "pipe"]
44832
- });
44833
- const goneBranches = branchOutput.split(`
44834
- `).filter((line) => line.includes(": gone]")).map((line) => line.trim().replace(/^[*+]\s+/, "").split(/\s+/)[0]).filter(Boolean);
44835
- for (const branch of goneBranches) {
44836
- try {
44837
- execFileSync("git", ["branch", "-d", branch], {
44838
- cwd: directory,
44839
- encoding: "utf-8",
44840
- stdio: ["pipe", "pipe", "pipe"]
44841
- });
44842
- prunedBranches.push(branch);
44843
- } catch {
44844
- pruneErrors.push(branch);
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
- "- Archived evidence bundles",
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 ? ` Warnings: ${warnings.join("; ")}.` : "";
45144
+ const warningMsg = warnings.length > 0 ? `
45145
+
45146
+ **Warnings:**
45147
+ ${warnings.map((w) => `- ${w}`).join(`
45148
+ `)}` : "";
44896
45149
  if (planAlreadyDone) {
44897
- return `\u2705 Session closed. Plan was already in a terminal state \u2014 cleanup steps applied.${warningMsg}`;
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 closed successfully. ${closedPhases.length} phase(s) closed, ${closedTasks.length} incomplete task(s) marked closed.${warningMsg}`;
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 = recommendations.find((r) => r.entry_id === entry.id);
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 recommendations) {
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 recommendations) {
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 child_process3 from "child_process";
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
- child_process3.execSync("git rev-parse --git-dir", {
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 full-auto from within an OpenCode session, or start a session first.";
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
- feedback = newFullAutoMode ? "Full-Auto Mode enabled" : "Full-Auto Mode disabled";
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 feedback;
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
- **Next Step:** Start a new OpenCode session, switch to your target model, and send: \`continue the previous work\``;
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 ESCALATION_PATTERNS = [
56165
- /Ready for Phase (?:\d+|\[?N\+1\]?)\?/i,
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 ESCALATION_PATTERNS) {
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, _escalationType, oversightAgentName) {
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 child_process4 from "child_process";
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 = child_process4.spawn(cmd, args2, {
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 child_process5 from "child_process";
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 = child_process5.execFileSync("git", numstatArgs, {
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 = child_process5.execFileSync("git", fullDiffArgs, {
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 = child_process5.execFileSync("git", ["show", `HEAD:${file3.path}`], {
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 = child_process5.execFileSync("git", ["show", `:${file3.path}`], {
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 = child_process5.execFileSync("git", ["show", `:${file3.path}`], {
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 = child_process5.execFileSync("git", ["show", `HEAD:${file3.path}`], {
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 = child_process5.execFileSync("git", ["show", `${base}:${file3.path}`], {
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 = child_process5.execFileSync("git", ["show", `HEAD:${file3.path}`], {
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 child_process6 from "child_process";
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
- child_process6.execFileSync("semgrep", ["--version"], {
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 = child_process6.spawn(command, args2, {
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 full-auto $ARGUMENTS",
72808
- description: "Use /swarm full-auto to toggle Full-Auto Mode for the active session [on|off]"
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",