opencode-swarm 6.38.0 → 6.40.1

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
@@ -27,7 +27,7 @@ Most AI coding tools let one model write code and ask that same model whether th
27
27
  ### Key Features
28
28
 
29
29
  - 🏗️ **11 specialized agents** — architect, coder, reviewer, test engineer, critic, critic_sounding_board, critic_drift_verifier, explorer, SME, docs, designer
30
- - 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval
30
+ - 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval (bypassed in turbo mode)
31
31
  - 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion (bypassed in turbo mode)
32
32
  - 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
33
33
  - 🌐 **11 languages** — TypeScript, Python, Go, Rust, Java, Kotlin, C#, C/C++, Swift, Dart, Ruby
package/dist/cli/index.js CHANGED
@@ -14590,7 +14590,8 @@ var init_plan_schema = __esm(() => {
14590
14590
  id: exports_external.number().int().min(1),
14591
14591
  name: exports_external.string().min(1),
14592
14592
  status: PhaseStatusSchema.default("pending"),
14593
- tasks: exports_external.array(TaskSchema).default([])
14593
+ tasks: exports_external.array(TaskSchema).default([]),
14594
+ required_agents: exports_external.array(exports_external.string()).optional()
14594
14595
  });
14595
14596
  PlanSchema = exports_external.object({
14596
14597
  schema_version: exports_external.literal("1.0.0"),
@@ -17319,8 +17320,8 @@ function getTaskBlockers(task, summary, status) {
17319
17320
  }
17320
17321
  return blockers;
17321
17322
  }
17322
- async function buildTaskSummary(task, taskId) {
17323
- const result = await loadEvidence(".", taskId);
17323
+ async function buildTaskSummary(directory, task, taskId) {
17324
+ const result = await loadEvidence(directory, taskId);
17324
17325
  const bundle = result.status === "found" ? result.bundle : null;
17325
17326
  const phase = task?.phase ?? 0;
17326
17327
  const status = getTaskStatus(task, bundle);
@@ -17349,18 +17350,18 @@ async function buildTaskSummary(task, taskId) {
17349
17350
  lastEvidenceTimestamp: lastTimestamp
17350
17351
  };
17351
17352
  }
17352
- async function buildPhaseSummary(phase) {
17353
- const taskIds = await listEvidenceTaskIds(".");
17353
+ async function buildPhaseSummary(directory, phase) {
17354
+ const taskIds = await listEvidenceTaskIds(directory);
17354
17355
  const phaseTaskIds = new Set(phase.tasks.map((t) => t.id));
17355
17356
  const taskSummaries = [];
17356
17357
  const _taskMap = new Map(phase.tasks.map((t) => [t.id, t]));
17357
17358
  for (const task of phase.tasks) {
17358
- const summary = await buildTaskSummary(task, task.id);
17359
+ const summary = await buildTaskSummary(directory, task, task.id);
17359
17360
  taskSummaries.push(summary);
17360
17361
  }
17361
17362
  const extraTaskIds = taskIds.filter((id) => !phaseTaskIds.has(id));
17362
17363
  for (const taskId of extraTaskIds) {
17363
- const summary = await buildTaskSummary(undefined, taskId);
17364
+ const summary = await buildTaskSummary(directory, undefined, taskId);
17364
17365
  if (summary.phase === phase.id) {
17365
17366
  taskSummaries.push(summary);
17366
17367
  }
@@ -17461,7 +17462,7 @@ async function buildEvidenceSummary(directory, currentPhase) {
17461
17462
  let totalTasks = 0;
17462
17463
  let completedTasks = 0;
17463
17464
  for (const phase of phasesToProcess) {
17464
- const summary = await buildPhaseSummary(phase);
17465
+ const summary = await buildPhaseSummary(directory, phase);
17465
17466
  phaseSummaries.push(summary);
17466
17467
  totalTasks += summary.totalTasks;
17467
17468
  completedTasks += summary.completedTasks;
@@ -17620,7 +17621,13 @@ var TOOL_NAMES = [
17620
17621
  "update_task_status",
17621
17622
  "write_retro",
17622
17623
  "declare_scope",
17623
- "knowledge_query"
17624
+ "knowledge_query",
17625
+ "doc_scan",
17626
+ "doc_extract",
17627
+ "curator_analyze",
17628
+ "knowledgeAdd",
17629
+ "knowledgeRecall",
17630
+ "knowledgeRemove"
17624
17631
  ];
17625
17632
  var TOOL_NAME_SET = new Set(TOOL_NAMES);
17626
17633
 
@@ -17645,6 +17652,7 @@ var AGENT_TOOL_MAP = {
17645
17652
  architect: [
17646
17653
  "checkpoint",
17647
17654
  "check_gate_status",
17655
+ "completion_verify",
17648
17656
  "complexity_hotspots",
17649
17657
  "detect_domains",
17650
17658
  "evidence_check",
@@ -17656,6 +17664,7 @@ var AGENT_TOOL_MAP = {
17656
17664
  "diff",
17657
17665
  "pkg_audit",
17658
17666
  "pre_check_batch",
17667
+ "quality_budget",
17659
17668
  "retrieve_summary",
17660
17669
  "save_plan",
17661
17670
  "schema_drift",
@@ -17665,7 +17674,19 @@ var AGENT_TOOL_MAP = {
17665
17674
  "todo_extract",
17666
17675
  "update_task_status",
17667
17676
  "write_retro",
17668
- "declare_scope"
17677
+ "declare_scope",
17678
+ "sast_scan",
17679
+ "sbom_generate",
17680
+ "build_check",
17681
+ "syntax_check",
17682
+ "placeholder_scan",
17683
+ "phase_complete",
17684
+ "doc_scan",
17685
+ "doc_extract",
17686
+ "curator_analyze",
17687
+ "knowledgeAdd",
17688
+ "knowledgeRecall",
17689
+ "knowledgeRemove"
17669
17690
  ],
17670
17691
  explorer: [
17671
17692
  "complexity_hotspots",
@@ -17676,7 +17697,9 @@ var AGENT_TOOL_MAP = {
17676
17697
  "retrieve_summary",
17677
17698
  "schema_drift",
17678
17699
  "symbols",
17679
- "todo_extract"
17700
+ "todo_extract",
17701
+ "doc_scan",
17702
+ "knowledgeRecall"
17680
17703
  ],
17681
17704
  coder: [
17682
17705
  "diff",
@@ -17684,7 +17707,11 @@ var AGENT_TOOL_MAP = {
17684
17707
  "lint",
17685
17708
  "symbols",
17686
17709
  "extract_code_blocks",
17687
- "retrieve_summary"
17710
+ "retrieve_summary",
17711
+ "build_check",
17712
+ "syntax_check",
17713
+ "knowledgeAdd",
17714
+ "knowledgeRecall"
17688
17715
  ],
17689
17716
  test_engineer: [
17690
17717
  "test_runner",
@@ -17694,7 +17721,9 @@ var AGENT_TOOL_MAP = {
17694
17721
  "retrieve_summary",
17695
17722
  "imports",
17696
17723
  "complexity_hotspots",
17697
- "pkg_audit"
17724
+ "pkg_audit",
17725
+ "build_check",
17726
+ "syntax_check"
17698
17727
  ],
17699
17728
  sme: [
17700
17729
  "complexity_hotspots",
@@ -17703,7 +17732,8 @@ var AGENT_TOOL_MAP = {
17703
17732
  "imports",
17704
17733
  "retrieve_summary",
17705
17734
  "schema_drift",
17706
- "symbols"
17735
+ "symbols",
17736
+ "knowledgeRecall"
17707
17737
  ],
17708
17738
  reviewer: [
17709
17739
  "diff",
@@ -17716,28 +17746,34 @@ var AGENT_TOOL_MAP = {
17716
17746
  "complexity_hotspots",
17717
17747
  "retrieve_summary",
17718
17748
  "extract_code_blocks",
17719
- "test_runner"
17749
+ "test_runner",
17750
+ "sast_scan",
17751
+ "placeholder_scan",
17752
+ "knowledgeRecall"
17720
17753
  ],
17721
17754
  critic: [
17722
17755
  "complexity_hotspots",
17723
17756
  "detect_domains",
17724
17757
  "imports",
17725
17758
  "retrieve_summary",
17726
- "symbols"
17759
+ "symbols",
17760
+ "knowledgeRecall"
17727
17761
  ],
17728
17762
  critic_sounding_board: [
17729
17763
  "complexity_hotspots",
17730
17764
  "detect_domains",
17731
17765
  "imports",
17732
17766
  "retrieve_summary",
17733
- "symbols"
17767
+ "symbols",
17768
+ "knowledgeRecall"
17734
17769
  ],
17735
17770
  critic_drift_verifier: [
17736
17771
  "complexity_hotspots",
17737
17772
  "detect_domains",
17738
17773
  "imports",
17739
17774
  "retrieve_summary",
17740
- "symbols"
17775
+ "symbols",
17776
+ "knowledgeRecall"
17741
17777
  ],
17742
17778
  docs: [
17743
17779
  "detect_domains",
@@ -17747,9 +17783,15 @@ var AGENT_TOOL_MAP = {
17747
17783
  "retrieve_summary",
17748
17784
  "schema_drift",
17749
17785
  "symbols",
17750
- "todo_extract"
17786
+ "todo_extract",
17787
+ "knowledgeRecall"
17751
17788
  ],
17752
- designer: ["extract_code_blocks", "retrieve_summary", "symbols"]
17789
+ designer: [
17790
+ "extract_code_blocks",
17791
+ "retrieve_summary",
17792
+ "symbols",
17793
+ "knowledgeRecall"
17794
+ ]
17753
17795
  };
17754
17796
  for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
17755
17797
  const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
@@ -18138,8 +18180,8 @@ var PlanCursorConfigSchema = exports_external.object({
18138
18180
  });
18139
18181
  var CheckpointConfigSchema = exports_external.object({
18140
18182
  enabled: exports_external.boolean().default(true),
18141
- auto_checkpoint_threshold: exports_external.number().min(1).max(20).default(3)
18142
- });
18183
+ auto_checkpoint_threshold: exports_external.number().int().min(1).max(20).default(3)
18184
+ }).strict();
18143
18185
  var AutomationModeSchema = exports_external.enum(["manual", "hybrid", "auto"]);
18144
18186
  var AutomationCapabilitiesSchema = exports_external.object({
18145
18187
  plan_sync: exports_external.boolean().default(true),
@@ -18259,7 +18301,8 @@ var PluginConfigSchema = exports_external.object({
18259
18301
  block_on_threshold: exports_external.boolean().default(false).describe("If true, block phase completion when threshold exceeded. Default: advisory only.")
18260
18302
  }).optional(),
18261
18303
  incremental_verify: IncrementalVerifyConfigSchema.optional(),
18262
- compaction_service: CompactionConfigSchema.optional()
18304
+ compaction_service: CompactionConfigSchema.optional(),
18305
+ turbo_mode: exports_external.boolean().default(false).optional()
18263
18306
  });
18264
18307
 
18265
18308
  // src/config/loader.ts
@@ -18362,6 +18405,15 @@ function loadPluginConfig(directory) {
18362
18405
  }
18363
18406
  return result.data;
18364
18407
  }
18408
+ function loadPluginConfigWithMeta(directory) {
18409
+ const userConfigPath = path.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
18410
+ const projectConfigPath = path.join(directory, ".opencode", CONFIG_FILENAME);
18411
+ const userResult = loadRawConfigFromPath(userConfigPath);
18412
+ const projectResult = loadRawConfigFromPath(projectConfigPath);
18413
+ const loadedFromFile = userResult.fileExisted || projectResult.fileExisted;
18414
+ const config2 = loadPluginConfig(directory);
18415
+ return { config: config2, loadedFromFile };
18416
+ }
18365
18417
 
18366
18418
  // src/commands/archive.ts
18367
18419
  init_manager();
@@ -18784,6 +18836,9 @@ async function handleBenchmarkCommand(directory, args) {
18784
18836
  `);
18785
18837
  }
18786
18838
 
18839
+ // src/commands/checkpoint.ts
18840
+ init_zod();
18841
+
18787
18842
  // src/tools/checkpoint.ts
18788
18843
  import { spawnSync } from "child_process";
18789
18844
  import * as fs3 from "fs";
@@ -31109,6 +31164,10 @@ function tool(input) {
31109
31164
  return input;
31110
31165
  }
31111
31166
  tool.schema = exports_external2;
31167
+
31168
+ // src/config/index.ts
31169
+ init_evidence_schema();
31170
+ init_plan_schema();
31112
31171
  // src/tools/create-tool.ts
31113
31172
  function createSwarmTool(opts) {
31114
31173
  return tool({
@@ -31233,6 +31292,11 @@ function isGitRepo() {
31233
31292
  }
31234
31293
  function handleSave(label, directory) {
31235
31294
  try {
31295
+ let maxCheckpoints = 20;
31296
+ try {
31297
+ const { config: config3 } = loadPluginConfigWithMeta(directory);
31298
+ maxCheckpoints = config3.checkpoint?.auto_checkpoint_threshold ?? maxCheckpoints;
31299
+ } catch {}
31236
31300
  const log2 = readCheckpointLog(directory);
31237
31301
  const existingCheckpoint = log2.checkpoints.find((c) => c.label === label);
31238
31302
  if (existingCheckpoint) {
@@ -31352,7 +31416,7 @@ var checkpoint = createSwarmTool({
31352
31416
  let label;
31353
31417
  try {
31354
31418
  action = String(args.action);
31355
- label = args.label !== undefined ? String(args.label) : undefined;
31419
+ label = args.label !== undefined && args.label !== null ? String(args.label) : undefined;
31356
31420
  } catch {
31357
31421
  return JSON.stringify({
31358
31422
  action: "unknown",
@@ -31405,6 +31469,22 @@ var checkpoint = createSwarmTool({
31405
31469
  });
31406
31470
 
31407
31471
  // src/commands/checkpoint.ts
31472
+ var CheckpointResultSchema = exports_external.object({
31473
+ action: exports_external.string().optional(),
31474
+ success: exports_external.boolean(),
31475
+ error: exports_external.string().optional(),
31476
+ checkpoints: exports_external.array(exports_external.unknown()).optional()
31477
+ }).passthrough();
31478
+ function safeParseResult(result) {
31479
+ const parsed = CheckpointResultSchema.safeParse(JSON.parse(result));
31480
+ if (!parsed.success) {
31481
+ return {
31482
+ success: false,
31483
+ error: `Invalid response: ${parsed.error.message}`
31484
+ };
31485
+ }
31486
+ return parsed.data;
31487
+ }
31408
31488
  async function handleCheckpointCommand(directory, args) {
31409
31489
  const subcommand = args[0] || "list";
31410
31490
  const label = args[1];
@@ -31427,7 +31507,7 @@ async function handleSave2(directory, label) {
31427
31507
  const result = await checkpoint.execute({ action: "save", label }, {
31428
31508
  directory
31429
31509
  });
31430
- const parsed = JSON.parse(result);
31510
+ const parsed = safeParseResult(result);
31431
31511
  if (parsed.success) {
31432
31512
  return `\u2713 Checkpoint saved: "${label}"`;
31433
31513
  } else {
@@ -31446,7 +31526,7 @@ async function handleRestore2(directory, label) {
31446
31526
  const result = await checkpoint.execute({ action: "restore", label }, {
31447
31527
  directory
31448
31528
  });
31449
- const parsed = JSON.parse(result);
31529
+ const parsed = safeParseResult(result);
31450
31530
  if (parsed.success) {
31451
31531
  return `\u2713 Restored to checkpoint: "${label}"`;
31452
31532
  } else {
@@ -31465,7 +31545,7 @@ async function handleDelete2(directory, label) {
31465
31545
  const result = await checkpoint.execute({ action: "delete", label }, {
31466
31546
  directory
31467
31547
  });
31468
- const parsed = JSON.parse(result);
31548
+ const parsed = safeParseResult(result);
31469
31549
  if (parsed.success) {
31470
31550
  return `\u2713 Checkpoint deleted: "${label}"`;
31471
31551
  } else {
@@ -31481,7 +31561,7 @@ async function handleList2(directory) {
31481
31561
  const result = await checkpoint.execute({ action: "list" }, {
31482
31562
  directory
31483
31563
  });
31484
- const parsed = JSON.parse(result);
31564
+ const parsed = safeParseResult(result);
31485
31565
  if (!parsed.success) {
31486
31566
  return `Error: ${parsed.error || "Failed to list checkpoints"}`;
31487
31567
  }
@@ -34179,7 +34259,8 @@ async function extractFromLegacy(directory) {
34179
34259
  mappedStatus = "complete";
34180
34260
  else if (status === "IN PROGRESS")
34181
34261
  mappedStatus = "in_progress";
34182
- const headerLineIndex = lines.indexOf(match[0]);
34262
+ const headerLineIndex = planContent.substring(0, match.index).split(`
34263
+ `).length - 1;
34183
34264
  let completed = 0;
34184
34265
  let total = 0;
34185
34266
  if (headerLineIndex !== -1) {
@@ -34574,9 +34655,9 @@ async function getPlanData(directory, phaseArg) {
34574
34655
  return {
34575
34656
  hasPlan: true,
34576
34657
  fullMarkdown,
34577
- requestedPhase: NaN,
34658
+ requestedPhase: null,
34578
34659
  phaseMarkdown: null,
34579
- errorMessage: null,
34660
+ errorMessage: `Invalid phase number: "${phaseArg}"`,
34580
34661
  isLegacy: false
34581
34662
  };
34582
34663
  }
@@ -34627,9 +34708,9 @@ async function getPlanData(directory, phaseArg) {
34627
34708
  return {
34628
34709
  hasPlan: true,
34629
34710
  fullMarkdown: planContent,
34630
- requestedPhase: NaN,
34711
+ requestedPhase: null,
34631
34712
  phaseMarkdown: null,
34632
- errorMessage: null,
34713
+ errorMessage: `Invalid phase number: "${phaseArg}"`,
34633
34714
  isLegacy: true
34634
34715
  };
34635
34716
  }
@@ -35568,6 +35649,59 @@ LANGUAGE_REGISTRY.register({
35568
35649
  ]
35569
35650
  }
35570
35651
  });
35652
+ LANGUAGE_REGISTRY.register({
35653
+ id: "php",
35654
+ displayName: "PHP",
35655
+ tier: 3,
35656
+ extensions: [".php", ".phtml"],
35657
+ treeSitter: { grammarId: "php", wasmFile: "tree-sitter-php.wasm" },
35658
+ build: {
35659
+ detectFiles: ["composer.json"],
35660
+ commands: []
35661
+ },
35662
+ test: {
35663
+ detectFiles: ["phpunit.xml", "phpunit.xml.dist"],
35664
+ frameworks: [
35665
+ {
35666
+ name: "PHPUnit",
35667
+ detect: "phpunit.xml",
35668
+ cmd: "vendor/bin/phpunit",
35669
+ priority: 1
35670
+ }
35671
+ ]
35672
+ },
35673
+ lint: {
35674
+ detectFiles: [".php-cs-fixer.php", "phpcs.xml"],
35675
+ linters: [
35676
+ {
35677
+ name: "PHP-CS-Fixer",
35678
+ detect: ".php-cs-fixer.php",
35679
+ cmd: "vendor/bin/php-cs-fixer fix --dry-run --diff",
35680
+ priority: 1
35681
+ }
35682
+ ]
35683
+ },
35684
+ audit: {
35685
+ detectFiles: ["composer.lock"],
35686
+ command: "composer audit --format=json",
35687
+ outputFormat: "json"
35688
+ },
35689
+ sast: { nativeRuleSet: "php", semgrepSupport: "ga" },
35690
+ prompts: {
35691
+ coderConstraints: [
35692
+ "Follow PSR-12 coding standards",
35693
+ "Use strict types declaration: declare(strict_types=1)",
35694
+ "Prefer type hints and return type declarations on all functions",
35695
+ "Use dependency injection over static methods and singletons"
35696
+ ],
35697
+ reviewerChecklist: [
35698
+ "Verify no user input reaches SQL queries without parameterised binding",
35699
+ "Check for XSS \u2014 all output must be escaped with htmlspecialchars()",
35700
+ "Confirm no eval(), exec(), or shell_exec() with user-controlled input",
35701
+ "Validate proper error handling \u2014 no bare catch blocks that swallow errors"
35702
+ ]
35703
+ }
35704
+ });
35571
35705
 
35572
35706
  // src/lang/detector.ts
35573
35707
  async function detectProjectLanguages(projectDir) {
@@ -35947,6 +36081,38 @@ var build_discovery = tool({
35947
36081
 
35948
36082
  // src/tools/lint.ts
35949
36083
  init_utils();
36084
+
36085
+ // src/utils/path-security.ts
36086
+ function containsPathTraversal(str) {
36087
+ if (/\.\.[/\\]/.test(str))
36088
+ return true;
36089
+ if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
36090
+ return true;
36091
+ if (/%2e%2e/i.test(str))
36092
+ return true;
36093
+ if (/%2e\./i.test(str))
36094
+ return true;
36095
+ if (/%2e/i.test(str) && /\.\./.test(str))
36096
+ return true;
36097
+ if (/%252e%252e/i.test(str))
36098
+ return true;
36099
+ if (/\uff0e/.test(str))
36100
+ return true;
36101
+ if (/\u3002/.test(str))
36102
+ return true;
36103
+ if (/\uff65/.test(str))
36104
+ return true;
36105
+ if (/%2f/i.test(str))
36106
+ return true;
36107
+ if (/%5c/i.test(str))
36108
+ return true;
36109
+ return false;
36110
+ }
36111
+ function containsControlChars(str) {
36112
+ return /[\0\t\r\n]/.test(str);
36113
+ }
36114
+
36115
+ // src/tools/lint.ts
35950
36116
  var MAX_OUTPUT_BYTES = 512000;
35951
36117
  var MAX_COMMAND_LENGTH = 500;
35952
36118
  function validateArgs(args) {
@@ -36119,7 +36285,7 @@ async function detectAvailableLinter(directory) {
36119
36285
  async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
36120
36286
  const DETECT_TIMEOUT = 2000;
36121
36287
  try {
36122
- const biomeProc = Bun.spawn(["npx", "biome", "--version"], {
36288
+ const biomeProc = Bun.spawn([biomeBin, "--version"], {
36123
36289
  stdout: "pipe",
36124
36290
  stderr: "pipe"
36125
36291
  });
@@ -36133,7 +36299,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
36133
36299
  }
36134
36300
  } catch {}
36135
36301
  try {
36136
- const eslintProc = Bun.spawn(["npx", "eslint", "--version"], {
36302
+ const eslintProc = Bun.spawn([eslintBin, "--version"], {
36137
36303
  stdout: "pipe",
36138
36304
  stderr: "pipe"
36139
36305
  });
@@ -36516,19 +36682,6 @@ function isHighEntropyString(str) {
36516
36682
  const entropy = calculateShannonEntropy(str);
36517
36683
  return entropy > 4;
36518
36684
  }
36519
- function containsPathTraversal(str) {
36520
- if (/\.\.[/\\]/.test(str))
36521
- return true;
36522
- if (/[/\\]\.\.$/.test(str) || str === "..")
36523
- return true;
36524
- if (/\.\.[/\\]/.test(path18.normalize(str.replace(/\*/g, "x"))))
36525
- return true;
36526
- if (str.includes("%2e%2e") || str.includes("%2E%2E"))
36527
- return true;
36528
- if (str.includes("..") && /%2e/i.test(str))
36529
- return true;
36530
- return false;
36531
- }
36532
36685
  function validateExcludePattern(exc) {
36533
36686
  if (exc.length === 0)
36534
36687
  return null;
@@ -36581,9 +36734,6 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
36581
36734
  }
36582
36735
  return false;
36583
36736
  }
36584
- function containsControlChars(str) {
36585
- return /[\0\t\r\n]/.test(str);
36586
- }
36587
36737
  function validateDirectoryInput(dir) {
36588
36738
  if (!dir || dir.length === 0) {
36589
36739
  return "directory is required";
@@ -37024,31 +37174,6 @@ var MAX_COMMAND_LENGTH2 = 500;
37024
37174
  var DEFAULT_TIMEOUT_MS = 60000;
37025
37175
  var MAX_TIMEOUT_MS = 300000;
37026
37176
  var MAX_SAFE_TEST_FILES = 50;
37027
- function containsPathTraversal2(str) {
37028
- if (/\.\.[/\\]/.test(str))
37029
- return true;
37030
- if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
37031
- return true;
37032
- if (/%2e%2e/i.test(str))
37033
- return true;
37034
- if (/%2e\./i.test(str))
37035
- return true;
37036
- if (/%2e/i.test(str) && /\.\./.test(str))
37037
- return true;
37038
- if (/%252e%252e/i.test(str))
37039
- return true;
37040
- if (/\uff0e/.test(str))
37041
- return true;
37042
- if (/\u3002/.test(str))
37043
- return true;
37044
- if (/\uff65/.test(str))
37045
- return true;
37046
- if (/%2f/i.test(str))
37047
- return true;
37048
- if (/%5c/i.test(str))
37049
- return true;
37050
- return false;
37051
- }
37052
37177
  function isAbsolutePath(str) {
37053
37178
  if (str.startsWith("/"))
37054
37179
  return true;
@@ -37060,9 +37185,6 @@ function isAbsolutePath(str) {
37060
37185
  return true;
37061
37186
  return false;
37062
37187
  }
37063
- function containsControlChars2(str) {
37064
- return /[\x00-\x08\x0a\x0b\x0c\x0d\x0e-\x1f\x7f\x80-\x9f]/.test(str);
37065
- }
37066
37188
  var POWERSHELL_METACHARACTERS = /[|;&`$(){}[\]<>"'#*?\x00-\x1f]/;
37067
37189
  function containsPowerShellMetacharacters(str) {
37068
37190
  return POWERSHELL_METACHARACTERS.test(str);
@@ -37084,9 +37206,9 @@ function validateArgs2(args) {
37084
37206
  return false;
37085
37207
  if (isAbsolutePath(f))
37086
37208
  return false;
37087
- if (containsPathTraversal2(f))
37209
+ if (containsPathTraversal(f))
37088
37210
  return false;
37089
- if (containsControlChars2(f))
37211
+ if (containsControlChars(f))
37090
37212
  return false;
37091
37213
  if (containsPowerShellMetacharacters(f))
37092
37214
  return false;
@@ -37909,7 +38031,7 @@ var test_runner = createSwarmTool({
37909
38031
  };
37910
38032
  return JSON.stringify(errorResult, null, 2);
37911
38033
  }
37912
- if (containsControlChars2(workingDir)) {
38034
+ if (containsControlChars(workingDir)) {
37913
38035
  const errorResult = {
37914
38036
  success: false,
37915
38037
  framework: "none",
@@ -37918,7 +38040,7 @@ var test_runner = createSwarmTool({
37918
38040
  };
37919
38041
  return JSON.stringify(errorResult, null, 2);
37920
38042
  }
37921
- if (containsPathTraversal2(workingDir)) {
38043
+ if (containsPathTraversal(workingDir)) {
37922
38044
  const errorResult = {
37923
38045
  success: false,
37924
38046
  framework: "none",
@@ -38194,12 +38316,13 @@ async function runLintCheck(dir, linter, timeoutMs) {
38194
38316
  const startTime = Date.now();
38195
38317
  try {
38196
38318
  const lintPromise = runLint(linter, "check", dir);
38319
+ let timeoutId;
38197
38320
  const timeoutPromise = new Promise((_, reject) => {
38198
- setTimeout(() => {
38321
+ timeoutId = setTimeout(() => {
38199
38322
  reject(new Error(`Lint check timed out after ${timeoutMs}ms`));
38200
38323
  }, timeoutMs);
38201
38324
  });
38202
- const result = await Promise.race([lintPromise, timeoutPromise]);
38325
+ const result = await Promise.race([lintPromise, timeoutPromise]).finally(() => clearTimeout(timeoutId));
38203
38326
  if (!result.success) {
38204
38327
  return {
38205
38328
  type: "lint",
@@ -39709,12 +39832,32 @@ function makeInitialState() {
39709
39832
  lastSnapshotAt: null
39710
39833
  };
39711
39834
  }
39712
- var state = makeInitialState();
39713
- function getCompactionMetrics() {
39714
- return {
39715
- compactionCount: state.observationCount + state.reflectionCount + state.emergencyCount,
39716
- lastSnapshotAt: state.lastSnapshotAt
39717
- };
39835
+ var sessionStates = new Map;
39836
+ function getSessionState(sessionId) {
39837
+ let state = sessionStates.get(sessionId);
39838
+ if (!state) {
39839
+ state = makeInitialState();
39840
+ sessionStates.set(sessionId, state);
39841
+ }
39842
+ return state;
39843
+ }
39844
+ function getCompactionMetrics(sessionId) {
39845
+ if (sessionId) {
39846
+ const state = getSessionState(sessionId);
39847
+ return {
39848
+ compactionCount: state.observationCount + state.reflectionCount + state.emergencyCount,
39849
+ lastSnapshotAt: state.lastSnapshotAt
39850
+ };
39851
+ }
39852
+ let total = 0;
39853
+ let lastSnapshot = null;
39854
+ for (const state of sessionStates.values()) {
39855
+ total += state.observationCount + state.reflectionCount + state.emergencyCount;
39856
+ if (state.lastSnapshotAt && (!lastSnapshot || state.lastSnapshotAt > lastSnapshot)) {
39857
+ lastSnapshot = state.lastSnapshotAt;
39858
+ }
39859
+ }
39860
+ return { compactionCount: total, lastSnapshotAt: lastSnapshot };
39718
39861
  }
39719
39862
 
39720
39863
  // src/services/context-budget-service.ts
@@ -39877,6 +40020,7 @@ async function handleTurboCommand(_directory, args, sessionID) {
39877
40020
  }
39878
40021
 
39879
40022
  // src/tools/write-retro.ts
40023
+ init_evidence_schema();
39880
40024
  init_manager();
39881
40025
  async function executeWriteRetro(args, directory) {
39882
40026
  const phase = args.phase;
@@ -40110,8 +40254,9 @@ async function executeWriteRetro(args, directory) {
40110
40254
  };
40111
40255
  const taxonomy = [];
40112
40256
  try {
40113
- for (const taskSuffix of ["1", "2", "3", "4", "5"]) {
40114
- const phaseTaskId = `${phase}.${taskSuffix}`;
40257
+ const allTaskIds = await listEvidenceTaskIds(directory);
40258
+ const phaseTaskIds = allTaskIds.filter((id) => id.startsWith(`${phase}.`));
40259
+ for (const phaseTaskId of phaseTaskIds) {
40115
40260
  const result = await loadEvidence(directory, phaseTaskId);
40116
40261
  if (result.status !== "found")
40117
40262
  continue;
@@ -40145,6 +40290,13 @@ async function executeWriteRetro(args, directory) {
40145
40290
  }
40146
40291
  } catch {}
40147
40292
  retroEntry.error_taxonomy = [...new Set(taxonomy)];
40293
+ const validationResult = RetrospectiveEvidenceSchema.safeParse(retroEntry);
40294
+ if (!validationResult.success) {
40295
+ return JSON.stringify({
40296
+ success: false,
40297
+ error: `Retrospective entry failed validation: ${validationResult.error.message}`
40298
+ }, null, 2);
40299
+ }
40148
40300
  try {
40149
40301
  await saveEvidence(directory, taskId, retroEntry);
40150
40302
  return JSON.stringify({
@@ -40209,7 +40361,7 @@ var write_retro = createSwarmTool({
40209
40361
  }
40210
40362
  });
40211
40363
 
40212
- // src/commands/write_retro.ts
40364
+ // src/commands/write-retro.ts
40213
40365
  async function handleWriteRetroCommand(directory, args) {
40214
40366
  if (args.length === 0 || !args[0] || args[0].trim() === "") {
40215
40367
  return `## Usage: /swarm write-retro <json>
@@ -29,7 +29,7 @@ export { handleSpecifyCommand } from './specify';
29
29
  export { handleStatusCommand } from './status';
30
30
  export { handleSyncPlanCommand } from './sync-plan';
31
31
  export { handleTurboCommand } from './turbo';
32
- export { handleWriteRetroCommand } from './write_retro';
32
+ export { handleWriteRetroCommand } from './write-retro';
33
33
  /**
34
34
  * Creates a command.execute.before handler for /swarm commands.
35
35
  * Uses factory pattern to close over directory and agents.