opencode-swarm 6.33.0 → 6.33.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -142,6 +142,30 @@ You only need to specify the agents you want to override. The `architect` inheri
142
142
 
143
143
  See the [full configuration reference](#configuration-reference) and the [free-tier model setup](#free-tier-opencode-zen-models) for more options.
144
144
 
145
+ ### Step 7 — Performance modes (optional)
146
+
147
+ Swarm runs optional quality hooks (slop detection, incremental verification, compaction) on every tool call. For faster iteration, you can skip these:
148
+
149
+ **Via slash command** (session-wide):
150
+ ```
151
+ /swarm turbo
152
+ ```
153
+
154
+ **Via config** (persistent):
155
+ ```json
156
+ {
157
+ "execution_mode": "fast"
158
+ }
159
+ ```
160
+
161
+ | Mode | Behavior |
162
+ |------|----------|
163
+ | `strict` | Runs all quality hooks (slop detector, incremental verify, compaction). Maximum safety, highest overhead. |
164
+ | `balanced` (default) | Skips optional quality hooks. Recommended for most workflows. |
165
+ | `fast` | Same as balanced. Reserved for future more aggressive optimizations. |
166
+
167
+ Use `strict` mode for critical security-sensitive changes. Switch to `balanced` for routine development.
168
+
145
169
  ---
146
170
 
147
171
  ## Common First-Run Questions
@@ -228,6 +252,55 @@ For production use, mix providers to maximize quality across writing vs. reviewi
228
252
  | MiniMax | `minimax-coding-plan/<model>` | `minimax-coding-plan/MiniMax-M2.5` |
229
253
  | Kimi | `kimi-for-coding/<model>` | `kimi-for-coding/k2p5` |
230
254
 
255
+ ### Model Fallback (v6.33)
256
+
257
+ When a transient model error occurs (rate limit, 429, 503, timeout, overloaded, model not found), Swarm can automatically switch to a fallback model.
258
+
259
+ **Configuration:**
260
+
261
+ ```json
262
+ {
263
+ "agents": {
264
+ "coder": {
265
+ "model": "anthropic/claude-opus-4-6",
266
+ "fallback_models": [
267
+ "anthropic/claude-sonnet-4-5",
268
+ "opencode/gpt-5-nano"
269
+ ]
270
+ }
271
+ }
272
+ }
273
+ ```
274
+
275
+ - **`fallback_models`** — Optional array of up to 3 fallback model identifiers. When the primary model fails with a transient error, Swarm injects a `MODEL FALLBACK` advisory and the next retry uses the next fallback model in the list.
276
+ - **Advisory injection** — When a transient error is detected, a `MODEL FALLBACK` advisory is injected into the architect's context: *"Transient model error detected (attempt N). The agent model may be rate-limited, overloaded, or temporarily unavailable. Consider retrying with a fallback model or waiting before retrying."*
277
+ - **Exhaustion guard** — After exhausting all fallbacks (`modelFallbackExhausted = true`), further transient errors do not spam additional advisories.
278
+ - **Reset on success** — Both `model_fallback_index` and `modelFallbackExhausted` reset to 0/false on the next successful tool execution.
279
+
280
+ ### Bounded Coder Revisions (v6.33)
281
+
282
+ When a task requires multiple coder attempts (e.g., reviewer rejections), Swarm tracks how many times the coder has been re-delegated for the same task and warns when limits are approached.
283
+
284
+ **Configuration:**
285
+
286
+ ```json
287
+ {
288
+ "guardrails": {
289
+ "max_coder_revisions": 5
290
+ }
291
+ }
292
+ ```
293
+
294
+ | Parameter | Type | Default | Description |
295
+ |-----------|------|---------|-------------|
296
+ | `max_coder_revisions` | integer | `5` | Maximum coder re-delegations per task before advisory warning (1–20) |
297
+
298
+ **Behavior:**
299
+ - **`coderRevisions` counter** — Incremented each time the coder delegation completes for the same task (reset on new task)
300
+ - **`revisionLimitHit` flag** — Set when `coderRevisions >= max_coder_revisions`
301
+ - **Advisory injection** — When the limit is hit, a `CODER REVISION LIMIT` advisory is injected: *"Agent has been revised N times (max: M) for task X. Escalate to user or consider a fundamentally different approach."*
302
+ - **Persistence** — Both `coderRevisions` and `revisionLimitHit` are serialized/deserialized in session snapshots
303
+
231
304
  ## Useful Commands
232
305
 
233
306
  | Command | What It Does |
@@ -395,7 +468,8 @@ Every completed task writes structured evidence to `.swarm/evidence/`:
395
468
  | review | Verdict, risk level, specific issues |
396
469
  | test | Pass/fail counts, coverage %, failure messages |
397
470
  | diff | Files changed, additions/deletions |
398
- | retrospective | Phase metrics, lessons learned (injected into next phase) |
471
+ | retrospective | Phase metrics, lessons learned, error taxonomy classification (injected into next phase) |
472
+ | secretscan | Secret scan results: findings count, files scanned, skipped files (v6.33) |
399
473
 
400
474
  </details>
401
475
 
@@ -635,11 +709,11 @@ Config file location: `~/.config/opencode/opencode-swarm.json` (global) or `.ope
635
709
  {
636
710
  "agents": {
637
711
  "architect": { "model": "anthropic/claude-opus-4-6" },
638
- "coder": { "model": "minimax-coding-plan/MiniMax-M2.5" },
712
+ "coder": { "model": "minimax-coding-plan/MiniMax-M2.5", "fallback_models": ["minimax-coding-plan/MiniMax-M2.1"] },
639
713
  "explorer": { "model": "minimax-coding-plan/MiniMax-M2.1" },
640
714
  "sme": { "model": "kimi-for-coding/k2p5" },
641
715
  "critic": { "model": "zai-coding-plan/glm-5" },
642
- "reviewer": { "model": "zai-coding-plan/glm-5" },
716
+ "reviewer": { "model": "zai-coding-plan/glm-5", "fallback_models": ["opencode/big-pickle"] },
643
717
  "test_engineer": { "model": "minimax-coding-plan/MiniMax-M2.5" },
644
718
  "docs": { "model": "zai-coding-plan/glm-4.7-flash" },
645
719
  "designer": { "model": "kimi-for-coding/k2p5" }
@@ -833,14 +907,14 @@ Swarm limits which tools each agent can access based on their role. This prevent
833
907
 
834
908
  | Agent | Tools | Count | Rationale |
835
909
  |-------|-------|:---:|-----------|
836
- | **architect** | All 21 tools | 21 | Orchestrator needs full visibility |
910
+ | **architect** | All 23 tools | 23 | Orchestrator needs full visibility |
837
911
  | **reviewer** | diff, imports, lint, pkg_audit, pre_check_batch, secretscan, symbols, complexity_hotspots, retrieve_summary, extract_code_blocks, test_runner | 11 | Security-focused QA |
838
912
  | **coder** | diff, imports, lint, symbols, extract_code_blocks, retrieve_summary | 6 | Write-focused, minimal read tools |
839
913
  | **test_engineer** | test_runner, diff, symbols, extract_code_blocks, retrieve_summary, imports, complexity_hotspots, pkg_audit | 8 | Testing and verification |
840
914
  | **explorer** | complexity_hotspots, detect_domains, extract_code_blocks, gitingest, imports, retrieve_summary, schema_drift, symbols, todo_extract | 9 | Discovery and analysis |
841
915
  | **sme** | complexity_hotspots, detect_domains, extract_code_blocks, imports, retrieve_summary, schema_drift, symbols | 7 | Domain expertise research |
842
916
  | **critic** | complexity_hotspots, detect_domains, imports, retrieve_summary, symbols | 5 | Plan review, minimal toolset |
843
- | **docs** | detect_domains, extract_code_blocks, gitingest, imports, retrieve_summary, schema_drift, symbols, todo_extract | 8 | Documentation synthesis |
917
+ | **docs** | detect_domains, doc_extract, doc_scan, extract_code_blocks, gitingest, imports, retrieve_summary, schema_drift, symbols, todo_extract | 10 | Documentation synthesis and discovery |
844
918
  | **designer** | extract_code_blocks, retrieve_summary, symbols | 3 | UI-focused, minimal toolset |
845
919
 
846
920
  ### Configuration
@@ -900,13 +974,17 @@ The following tools can be assigned to agents via overrides:
900
974
  | `checkpoint` | Save/restore git checkpoints |
901
975
  | `check_gate_status` | Read-only query of task gate status |
902
976
  | `complexity_hotspots` | Identify high-risk code areas |
977
+ | `declare_scope` | Pre-declare the file scope for the next coder delegation (architect-only); violations trigger warnings |
903
978
  | `detect_domains` | Detect SME domains from text |
904
979
  | `diff` | Analyze git diffs and changes |
980
+ | `doc_extract` | Extract actionable constraints from project documentation relevant to current task (Jaccard bigram scoring + dedup) |
981
+ | `doc_scan` | Scan project documentation and build index manifest at `.swarm/doc-manifest.json` (mtime-based caching) |
905
982
  | `evidence_check` | Verify task evidence |
906
983
  | `extract_code_blocks` | Extract code from markdown |
907
984
  | `gitingest` | Ingest external repositories |
908
985
  | `imports` | Analyze import relationships |
909
986
  | `lint` | Run project linters |
987
+ | `phase_complete` | Enforces phase completion, verifies required agents, logs events, resets state |
910
988
  | `pkg_audit` | Security audit of dependencies |
911
989
  | `pre_check_batch` | Parallel pre-checks (lint, secrets, SAST, quality) |
912
990
  | `retrieve_summary` | Retrieve summarized tool outputs |
@@ -917,8 +995,6 @@ The following tools can be assigned to agents via overrides:
917
995
  | `update_task_status` | Mark plan tasks as pending/in_progress/completed/blocked; track phase progress |
918
996
  | `todo_extract` | Extract TODO/FIXME comments |
919
997
  | `write_retro` | Document phase retrospectives via the phase_complete workflow; capture lessons learned |
920
- | `phase_complete` | Enforces phase completion, verifies required agents, logs events, resets state |
921
- | `declare_scope` | Pre-declare the file scope for the next coder delegation (architect-only); violations trigger warnings |
922
998
 
923
999
  ---
924
1000
 
package/dist/cli/index.js CHANGED
@@ -13940,7 +13940,7 @@ function deepMerge(base, override) {
13940
13940
  var MAX_MERGE_DEPTH = 10;
13941
13941
 
13942
13942
  // src/config/evidence-schema.ts
13943
- var EVIDENCE_MAX_JSON_BYTES, EVIDENCE_MAX_PATCH_BYTES, EVIDENCE_MAX_TASK_BYTES, EvidenceTypeSchema, EvidenceVerdictSchema, BaseEvidenceSchema, ReviewEvidenceSchema, TestEvidenceSchema, DiffEvidenceSchema, ApprovalEvidenceSchema, NoteEvidenceSchema, RetrospectiveEvidenceSchema, SyntaxEvidenceSchema, PlaceholderEvidenceSchema, SastEvidenceSchema, SbomEvidenceSchema, BuildEvidenceSchema, QualityBudgetEvidenceSchema, EvidenceSchema, EvidenceBundleSchema;
13943
+ var EVIDENCE_MAX_JSON_BYTES, EVIDENCE_MAX_PATCH_BYTES, EVIDENCE_MAX_TASK_BYTES, EvidenceTypeSchema, EvidenceVerdictSchema, BaseEvidenceSchema, ReviewEvidenceSchema, TestEvidenceSchema, DiffEvidenceSchema, ApprovalEvidenceSchema, NoteEvidenceSchema, RetrospectiveEvidenceSchema, SyntaxEvidenceSchema, PlaceholderEvidenceSchema, SastEvidenceSchema, SbomEvidenceSchema, BuildEvidenceSchema, QualityBudgetEvidenceSchema, SecretscanEvidenceSchema, EvidenceSchema, EvidenceBundleSchema;
13944
13944
  var init_evidence_schema = __esm(() => {
13945
13945
  init_zod();
13946
13946
  EVIDENCE_MAX_JSON_BYTES = 500 * 1024;
@@ -13958,7 +13958,8 @@ var init_evidence_schema = __esm(() => {
13958
13958
  "sast",
13959
13959
  "sbom",
13960
13960
  "build",
13961
- "quality_budget"
13961
+ "quality_budget",
13962
+ "secretscan"
13962
13963
  ]);
13963
13964
  EvidenceVerdictSchema = exports_external.enum([
13964
13965
  "pass",
@@ -14039,7 +14040,14 @@ var init_evidence_schema = __esm(() => {
14039
14040
  approach: exports_external.string().min(1),
14040
14041
  result: exports_external.enum(["success", "failure", "partial"]),
14041
14042
  abandoned_reason: exports_external.string().optional()
14042
- })).max(10).default([])
14043
+ })).max(10).default([]),
14044
+ error_taxonomy: exports_external.array(exports_external.enum([
14045
+ "planning_error",
14046
+ "interface_mismatch",
14047
+ "logic_error",
14048
+ "scope_creep",
14049
+ "gate_evasion"
14050
+ ])).default([])
14043
14051
  });
14044
14052
  SyntaxEvidenceSchema = BaseEvidenceSchema.extend({
14045
14053
  type: exports_external.literal("syntax"),
@@ -14150,6 +14158,13 @@ var init_evidence_schema = __esm(() => {
14150
14158
  })).default([]),
14151
14159
  files_analyzed: exports_external.array(exports_external.string())
14152
14160
  });
14161
+ SecretscanEvidenceSchema = BaseEvidenceSchema.extend({
14162
+ type: exports_external.literal("secretscan"),
14163
+ findings_count: exports_external.number().int().min(0).default(0),
14164
+ scan_directory: exports_external.string().optional(),
14165
+ files_scanned: exports_external.number().int().min(0).default(0),
14166
+ skipped_files: exports_external.number().int().min(0).default(0)
14167
+ });
14153
14168
  EvidenceSchema = exports_external.discriminatedUnion("type", [
14154
14169
  ReviewEvidenceSchema,
14155
14170
  TestEvidenceSchema,
@@ -14162,7 +14177,8 @@ var init_evidence_schema = __esm(() => {
14162
14177
  SastEvidenceSchema,
14163
14178
  SbomEvidenceSchema,
14164
14179
  BuildEvidenceSchema,
14165
- QualityBudgetEvidenceSchema
14180
+ QualityBudgetEvidenceSchema,
14181
+ SecretscanEvidenceSchema
14166
14182
  ]);
14167
14183
  EvidenceBundleSchema = exports_external.object({
14168
14184
  schema_version: exports_external.literal("1.0.0"),
@@ -14517,11 +14533,12 @@ var init_manager = __esm(() => {
14517
14533
  "sast",
14518
14534
  "sbom",
14519
14535
  "build",
14520
- "quality_budget"
14536
+ "quality_budget",
14537
+ "secretscan"
14521
14538
  ];
14522
14539
  TASK_ID_REGEX = /^\d+\.\d+(\.\d+)*$/;
14523
14540
  RETRO_TASK_ID_REGEX = /^retro-\d+$/;
14524
- INTERNAL_TOOL_ID_REGEX = /^(?:sast_scan|quality_budget|syntax_check|placeholder_scan|sbom_generate|build)$/;
14541
+ INTERNAL_TOOL_ID_REGEX = /^(?:sast_scan|quality_budget|syntax_check|placeholder_scan|sbom_generate|build|secretscan)$/;
14525
14542
  LEGACY_TASK_COMPLEXITY_MAP = {
14526
14543
  low: "simple",
14527
14544
  medium: "moderate",
@@ -17497,7 +17514,7 @@ var init_evidence_summary_service = __esm(() => {
17497
17514
  });
17498
17515
 
17499
17516
  // src/cli/index.ts
17500
- import * as fs11 from "fs";
17517
+ import * as fs12 from "fs";
17501
17518
  import * as os5 from "os";
17502
17519
  import * as path23 from "path";
17503
17520
 
@@ -17723,7 +17740,8 @@ for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
17723
17740
  var AgentOverrideConfigSchema = exports_external.object({
17724
17741
  model: exports_external.string().optional(),
17725
17742
  temperature: exports_external.number().min(0).max(2).optional(),
17726
- disabled: exports_external.boolean().optional()
17743
+ disabled: exports_external.boolean().optional(),
17744
+ fallback_models: exports_external.array(exports_external.string()).max(3).optional()
17727
17745
  });
17728
17746
  var SwarmConfigSchema = exports_external.object({
17729
17747
  name: exports_external.string().optional(),
@@ -18063,6 +18081,8 @@ var GuardrailsConfigSchema = exports_external.object({
18063
18081
  max_consecutive_errors: exports_external.number().min(2).max(20).default(5),
18064
18082
  warning_threshold: exports_external.number().min(0.1).max(0.9).default(0.75),
18065
18083
  idle_timeout_minutes: exports_external.number().min(5).max(240).default(60),
18084
+ no_op_warning_threshold: exports_external.number().min(1).max(100).default(15),
18085
+ max_coder_revisions: exports_external.number().int().min(1).max(20).default(5),
18066
18086
  qa_gates: exports_external.object({
18067
18087
  required_tools: exports_external.array(exports_external.string().min(1)).default([
18068
18088
  "diff",
@@ -18178,6 +18198,7 @@ var PluginConfigSchema = exports_external.object({
18178
18198
  pipeline: PipelineConfigSchema.optional(),
18179
18199
  phase_complete: PhaseCompleteConfigSchema.optional(),
18180
18200
  qa_retry_limit: exports_external.number().min(1).max(10).default(3),
18201
+ execution_mode: exports_external.enum(["strict", "balanced", "fast"]).default("balanced"),
18181
18202
  inject_phase_reminders: exports_external.boolean().default(true),
18182
18203
  hooks: HooksConfigSchema.optional(),
18183
18204
  gates: GateConfigSchema.optional(),
@@ -33931,6 +33952,8 @@ function formatHandoffMarkdown(data) {
33931
33952
  init_utils2();
33932
33953
  import { mkdirSync as mkdirSync4, renameSync as renameSync4 } from "fs";
33933
33954
  import * as path14 from "path";
33955
+ var pendingWrite = null;
33956
+ var lastWritePromise = Promise.resolve();
33934
33957
  function serializeAgentSession(s) {
33935
33958
  const gateLog = {};
33936
33959
  const rawGateLog = s.gateLog ?? new Map;
@@ -33992,7 +34015,11 @@ function serializeAgentSession(s) {
33992
34015
  taskWorkflowStates: Object.fromEntries(s.taskWorkflowStates ?? new Map),
33993
34016
  ...s.scopeViolationDetected !== undefined && {
33994
34017
  scopeViolationDetected: s.scopeViolationDetected
33995
- }
34018
+ },
34019
+ model_fallback_index: s.model_fallback_index ?? 0,
34020
+ modelFallbackExhausted: s.modelFallbackExhausted ?? false,
34021
+ coderRevisions: s.coderRevisions ?? 0,
34022
+ revisionLimitHit: s.revisionLimitHit ?? false
33996
34023
  };
33997
34024
  }
33998
34025
  async function writeSnapshot(directory, state) {
@@ -34015,7 +34042,20 @@ async function writeSnapshot(directory, state) {
34015
34042
  const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
34016
34043
  await Bun.write(tempPath, content);
34017
34044
  renameSync4(tempPath, resolvedPath);
34018
- } catch {}
34045
+ } catch (error93) {
34046
+ if (process.env.DEBUG_SWARM) {
34047
+ console.warn("[snapshot-writer] write failed:", error93 instanceof Error ? error93.message : String(error93));
34048
+ }
34049
+ }
34050
+ }
34051
+ async function flushPendingSnapshot(directory) {
34052
+ if (pendingWrite) {
34053
+ clearTimeout(pendingWrite);
34054
+ pendingWrite = null;
34055
+ await writeSnapshot(directory, swarmState).catch(() => {});
34056
+ } else {
34057
+ await lastWritePromise;
34058
+ }
34019
34059
  }
34020
34060
 
34021
34061
  // src/commands/handoff.ts
@@ -34027,6 +34067,7 @@ async function handleHandoffCommand(directory, _args) {
34027
34067
  await Bun.write(tempPath, markdown);
34028
34068
  renameSync5(tempPath, resolvedPath);
34029
34069
  await writeSnapshot(directory, swarmState);
34070
+ await flushPendingSnapshot(directory);
34030
34071
  return `## Handoff Brief Written
34031
34072
 
34032
34073
  Brief written to \`.swarm/handoff.md\`.
@@ -39322,6 +39363,37 @@ async function handleResetCommand(directory, args) {
39322
39363
  `);
39323
39364
  }
39324
39365
 
39366
+ // src/commands/reset-session.ts
39367
+ init_utils2();
39368
+ import * as fs10 from "fs";
39369
+ async function handleResetSessionCommand(directory, _args) {
39370
+ const results = [];
39371
+ try {
39372
+ const statePath = validateSwarmPath(directory, "session/state.json");
39373
+ if (fs10.existsSync(statePath)) {
39374
+ fs10.unlinkSync(statePath);
39375
+ results.push("\u2705 Deleted .swarm/session/state.json");
39376
+ } else {
39377
+ results.push("\u23ED\uFE0F state.json not found (already clean)");
39378
+ }
39379
+ } catch {
39380
+ results.push("\u274C Failed to delete state.json");
39381
+ }
39382
+ const sessionCount = swarmState.agentSessions.size;
39383
+ swarmState.agentSessions.clear();
39384
+ results.push(`\u2705 Cleared ${sessionCount} in-memory agent session(s)`);
39385
+ return [
39386
+ "## Session State Reset",
39387
+ "",
39388
+ ...results,
39389
+ "",
39390
+ "Session state cleared. Plan, evidence, and knowledge preserved.",
39391
+ "",
39392
+ "**Next step:** Start a new OpenCode session. The plugin will initialize fresh session state on startup."
39393
+ ].join(`
39394
+ `);
39395
+ }
39396
+
39325
39397
  // src/summaries/manager.ts
39326
39398
  init_utils2();
39327
39399
  init_utils();
@@ -39402,18 +39474,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
39402
39474
 
39403
39475
  // src/commands/rollback.ts
39404
39476
  init_utils2();
39405
- import * as fs10 from "fs";
39477
+ import * as fs11 from "fs";
39406
39478
  import * as path22 from "path";
39407
39479
  async function handleRollbackCommand(directory, args) {
39408
39480
  const phaseArg = args[0];
39409
39481
  if (!phaseArg) {
39410
39482
  const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
39411
- if (!fs10.existsSync(manifestPath2)) {
39483
+ if (!fs11.existsSync(manifestPath2)) {
39412
39484
  return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
39413
39485
  }
39414
39486
  let manifest2;
39415
39487
  try {
39416
- manifest2 = JSON.parse(fs10.readFileSync(manifestPath2, "utf-8"));
39488
+ manifest2 = JSON.parse(fs11.readFileSync(manifestPath2, "utf-8"));
39417
39489
  } catch {
39418
39490
  return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
39419
39491
  }
@@ -39435,12 +39507,12 @@ async function handleRollbackCommand(directory, args) {
39435
39507
  return "Error: Phase number must be a positive integer.";
39436
39508
  }
39437
39509
  const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
39438
- if (!fs10.existsSync(manifestPath)) {
39510
+ if (!fs11.existsSync(manifestPath)) {
39439
39511
  return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
39440
39512
  }
39441
39513
  let manifest;
39442
39514
  try {
39443
- manifest = JSON.parse(fs10.readFileSync(manifestPath, "utf-8"));
39515
+ manifest = JSON.parse(fs11.readFileSync(manifestPath, "utf-8"));
39444
39516
  } catch {
39445
39517
  return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
39446
39518
  }
@@ -39450,10 +39522,10 @@ async function handleRollbackCommand(directory, args) {
39450
39522
  return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
39451
39523
  }
39452
39524
  const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
39453
- if (!fs10.existsSync(checkpointDir)) {
39525
+ if (!fs11.existsSync(checkpointDir)) {
39454
39526
  return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
39455
39527
  }
39456
- const checkpointFiles = fs10.readdirSync(checkpointDir);
39528
+ const checkpointFiles = fs11.readdirSync(checkpointDir);
39457
39529
  if (checkpointFiles.length === 0) {
39458
39530
  return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
39459
39531
  }
@@ -39464,7 +39536,7 @@ async function handleRollbackCommand(directory, args) {
39464
39536
  const src = path22.join(checkpointDir, file3);
39465
39537
  const dest = path22.join(swarmDir, file3);
39466
39538
  try {
39467
- fs10.cpSync(src, dest, { recursive: true, force: true });
39539
+ fs11.cpSync(src, dest, { recursive: true, force: true });
39468
39540
  successes.push(file3);
39469
39541
  } catch (error93) {
39470
39542
  failures.push({ file: file3, error: error93.message });
@@ -39481,7 +39553,7 @@ async function handleRollbackCommand(directory, args) {
39481
39553
  timestamp: new Date().toISOString()
39482
39554
  };
39483
39555
  try {
39484
- fs10.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
39556
+ fs11.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
39485
39557
  `);
39486
39558
  } catch (error93) {
39487
39559
  console.error("Failed to write rollback event:", error93);
@@ -39524,11 +39596,11 @@ async function handleSimulateCommand(directory, args) {
39524
39596
  ];
39525
39597
  const report = reportLines.filter(Boolean).join(`
39526
39598
  `);
39527
- const fs11 = await import("fs/promises");
39599
+ const fs12 = await import("fs/promises");
39528
39600
  const path23 = await import("path");
39529
39601
  const reportPath = path23.join(directory, ".swarm", "simulate-report.md");
39530
- await fs11.mkdir(path23.dirname(reportPath), { recursive: true });
39531
- await fs11.writeFile(reportPath, report, "utf-8");
39602
+ await fs12.mkdir(path23.dirname(reportPath), { recursive: true });
39603
+ await fs12.writeFile(reportPath, report, "utf-8");
39532
39604
  return `${darkMatterPairs.length} hidden coupling pairs detected`;
39533
39605
  }
39534
39606
 
@@ -39993,8 +40065,46 @@ async function executeWriteRetro(args, directory) {
39993
40065
  top_rejection_reasons: args.top_rejection_reasons ?? [],
39994
40066
  lessons_learned: (args.lessons_learned ?? []).slice(0, 5),
39995
40067
  user_directives: [],
39996
- approaches_tried: []
40068
+ approaches_tried: [],
40069
+ error_taxonomy: []
39997
40070
  };
40071
+ const taxonomy = [];
40072
+ try {
40073
+ for (const taskSuffix of ["1", "2", "3", "4", "5"]) {
40074
+ const phaseTaskId = `${phase}.${taskSuffix}`;
40075
+ const result = await loadEvidence(directory, phaseTaskId);
40076
+ if (result.status !== "found")
40077
+ continue;
40078
+ const bundle = result.bundle;
40079
+ for (const entry of bundle.entries) {
40080
+ const e = entry;
40081
+ if (e.type === "review" && e.verdict === "fail") {
40082
+ const reasonParts = [];
40083
+ if (typeof e.summary === "string")
40084
+ reasonParts.push(e.summary);
40085
+ if (Array.isArray(e.issues)) {
40086
+ for (const iss of e.issues) {
40087
+ if (typeof iss.message === "string")
40088
+ reasonParts.push(iss.message);
40089
+ }
40090
+ }
40091
+ const reason = reasonParts.join(" ");
40092
+ if (/signature|type|contract|interface/i.test(reason)) {
40093
+ taxonomy.push("interface_mismatch");
40094
+ } else {
40095
+ taxonomy.push("logic_error");
40096
+ }
40097
+ } else if (e.type === "test" && e.verdict === "fail") {
40098
+ taxonomy.push("logic_error");
40099
+ } else if (e.agent === "scope_guard" && e.verdict === "fail") {
40100
+ taxonomy.push("scope_creep");
40101
+ } else if (e.agent === "loop_detector" && e.verdict === "fail") {
40102
+ taxonomy.push("gate_evasion");
40103
+ }
40104
+ }
40105
+ }
40106
+ } catch {}
40107
+ retroEntry.error_taxonomy = [...new Set(taxonomy)];
39998
40108
  try {
39999
40109
  await saveEvidence(directory, taskId, retroEntry);
40000
40110
  return JSON.stringify({
@@ -40209,6 +40319,10 @@ var COMMAND_REGISTRY = {
40209
40319
  handler: (ctx) => handleResetCommand(ctx.directory, ctx.args),
40210
40320
  description: "Clear swarm state files [--confirm]"
40211
40321
  },
40322
+ "reset-session": {
40323
+ handler: (ctx) => handleResetSessionCommand(ctx.directory, ctx.args),
40324
+ description: "Clear session state while preserving plan, evidence, and knowledge"
40325
+ },
40212
40326
  rollback: {
40213
40327
  handler: (ctx) => handleRollbackCommand(ctx.directory, ctx.args),
40214
40328
  description: "Restore swarm state to a checkpoint <phase>"
@@ -40282,13 +40396,13 @@ var OPENCODE_CONFIG_PATH = path23.join(CONFIG_DIR, "opencode.json");
40282
40396
  var PLUGIN_CONFIG_PATH = path23.join(CONFIG_DIR, "opencode-swarm.json");
40283
40397
  var PROMPTS_DIR = path23.join(CONFIG_DIR, "opencode-swarm");
40284
40398
  function ensureDir(dir) {
40285
- if (!fs11.existsSync(dir)) {
40286
- fs11.mkdirSync(dir, { recursive: true });
40399
+ if (!fs12.existsSync(dir)) {
40400
+ fs12.mkdirSync(dir, { recursive: true });
40287
40401
  }
40288
40402
  }
40289
40403
  function loadJson(filepath) {
40290
40404
  try {
40291
- const content = fs11.readFileSync(filepath, "utf-8");
40405
+ const content = fs12.readFileSync(filepath, "utf-8");
40292
40406
  const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
40293
40407
  return JSON.parse(stripped);
40294
40408
  } catch {
@@ -40296,7 +40410,7 @@ function loadJson(filepath) {
40296
40410
  }
40297
40411
  }
40298
40412
  function saveJson(filepath, data) {
40299
- fs11.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
40413
+ fs12.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
40300
40414
  `, "utf-8");
40301
40415
  }
40302
40416
  async function install() {
@@ -40329,7 +40443,7 @@ async function install() {
40329
40443
  saveJson(OPENCODE_CONFIG_PATH, opencodeConfig);
40330
40444
  console.log("\u2713 Added opencode-swarm to OpenCode plugins");
40331
40445
  console.log("\u2713 Disabled default OpenCode agents (explore, general)");
40332
- if (!fs11.existsSync(PLUGIN_CONFIG_PATH)) {
40446
+ if (!fs12.existsSync(PLUGIN_CONFIG_PATH)) {
40333
40447
  const defaultConfig = {
40334
40448
  agents: {
40335
40449
  coder: { model: "opencode/minimax-m2.5-free" },
@@ -40372,7 +40486,7 @@ async function uninstall() {
40372
40486
  `);
40373
40487
  const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
40374
40488
  if (!opencodeConfig) {
40375
- if (fs11.existsSync(OPENCODE_CONFIG_PATH)) {
40489
+ if (fs12.existsSync(OPENCODE_CONFIG_PATH)) {
40376
40490
  console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
40377
40491
  return 1;
40378
40492
  } else {
@@ -40404,13 +40518,13 @@ async function uninstall() {
40404
40518
  console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
40405
40519
  if (process.argv.includes("--clean")) {
40406
40520
  let cleaned = false;
40407
- if (fs11.existsSync(PLUGIN_CONFIG_PATH)) {
40408
- fs11.unlinkSync(PLUGIN_CONFIG_PATH);
40521
+ if (fs12.existsSync(PLUGIN_CONFIG_PATH)) {
40522
+ fs12.unlinkSync(PLUGIN_CONFIG_PATH);
40409
40523
  console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
40410
40524
  cleaned = true;
40411
40525
  }
40412
- if (fs11.existsSync(PROMPTS_DIR)) {
40413
- fs11.rmSync(PROMPTS_DIR, { recursive: true });
40526
+ if (fs12.existsSync(PROMPTS_DIR)) {
40527
+ fs12.rmSync(PROMPTS_DIR, { recursive: true });
40414
40528
  console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
40415
40529
  cleaned = true;
40416
40530
  }
@@ -21,6 +21,7 @@ export { handlePromoteCommand } from './promote';
21
21
  export type { CommandContext, CommandEntry, RegisteredCommand, } from './registry.js';
22
22
  export { COMMAND_REGISTRY, resolveCommand, VALID_COMMANDS, } from './registry.js';
23
23
  export { handleResetCommand } from './reset';
24
+ export { handleResetSessionCommand } from './reset-session';
24
25
  export { handleRetrieveCommand } from './retrieve';
25
26
  export { handleRollbackCommand } from './rollback';
26
27
  export { handleSimulateCommand } from './simulate';
@@ -104,6 +104,10 @@ export declare const COMMAND_REGISTRY: {
104
104
  readonly handler: (ctx: CommandContext) => Promise<string>;
105
105
  readonly description: "Clear swarm state files [--confirm]";
106
106
  };
107
+ readonly 'reset-session': {
108
+ readonly handler: (ctx: CommandContext) => Promise<string>;
109
+ readonly description: "Clear session state while preserving plan, evidence, and knowledge";
110
+ };
107
111
  readonly rollback: {
108
112
  readonly handler: (ctx: CommandContext) => Promise<string>;
109
113
  readonly description: "Restore swarm state to a checkpoint <phase>";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Handles the /swarm reset-session command.
3
+ * Deletes only the session state file (.swarm/session/state.json)
4
+ * and clears in-memory agent sessions. Preserves plan, evidence,
5
+ * and knowledge for continuity across sessions.
6
+ */
7
+ export declare function handleResetSessionCommand(directory: string, _args: string[]): Promise<string>;