opencode-swarm 7.50.3 → 7.51.0

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
@@ -735,6 +735,11 @@ Swarm provides tools for managing generated skill lifecycles:
735
735
  | mutation_test | Applies LLM-generated mutation patches to source files and runs tests to measure kill rate; verdict is pass/warn/fail based on configurable thresholds; used by the mutation_test gate (opt-in, off by default) |
736
736
  | generate_mutants | Architect-only: generates LLM-based mutation patches (5–10 per function across 6 types: off-by-one, null substitution, operator swap, guard removal, branch swap, side-effect deletion) for direct consumption by the mutation_test tool; returns SKIP verdict on LLM failure rather than throwing |
737
737
  | write_mutation_evidence | Architect-only: writes mutation gate results atomically to `.swarm/evidence/{phase}/mutation-gate.json`; accepts verdict (PASS/WARN/FAIL/SKIP), kill rate metrics, and optional survived mutant details; normalizes uppercase-to-lowercase before persisting |
738
+ | git_blame | Per-line git blame metadata (sha, author, date, summary) via `git blame --porcelain`; supports optional line range filtering |
739
+ | diff | Structured git diff with contract change detection; supports `summaryOnly` mode returning file list with additions/deletions counts |
740
+ | suggest_patch | Reviewer-safe structured patch suggestion; supports `format` parameter ('json' or 'unified') where unified outputs valid unified diff with `diff --git` headers, hunks, and context |
741
+ | test_runner | Auto-detect and run tests; supports `bail` parameter to inject framework-specific bail flags for early exit on first failure |
742
+ | symbols | Extract exported symbols from source files; supports `workspace` (boolean) and `name` (string) parameters for multi-file symbol search |
738
743
 
739
744
 
740
745
  All tools run locally. No Docker, no network calls, no external APIs.
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.50.3",
55
+ version: "7.51.0",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -16499,7 +16499,7 @@ var init_tool_metadata = __esm(() => {
16499
16499
  agents: ["architect", "reviewer", "critic_oversight"]
16500
16500
  },
16501
16501
  syntax_check: {
16502
- description: "syntax validation",
16502
+ description: "check syntax of source files using tree-sitter parsers across multiple languages, returning per-file errors",
16503
16503
  agents: ["architect", "coder", "test_engineer"]
16504
16504
  },
16505
16505
  placeholder_scan: {
@@ -16507,7 +16507,7 @@ var init_tool_metadata = __esm(() => {
16507
16507
  agents: ["architect", "reviewer"]
16508
16508
  },
16509
16509
  imports: {
16510
- description: "dependency audit",
16510
+ description: "find all consumers that import from a given file \u2014 use before refactoring shared modules to avoid breaking unseen dependents",
16511
16511
  agents: [
16512
16512
  "architect",
16513
16513
  "sme",
@@ -16525,11 +16525,11 @@ var init_tool_metadata = __esm(() => {
16525
16525
  ]
16526
16526
  },
16527
16527
  lint: {
16528
- description: "code quality",
16528
+ description: "run project linter in check or fix mode; supports biome, eslint, ruff, clippy, and more, returns structured results",
16529
16529
  agents: ["architect", "reviewer", "coder"]
16530
16530
  },
16531
16531
  secretscan: {
16532
- description: "secret detection",
16532
+ description: "scan for secrets (API keys, tokens, passwords) via regex and entropy; returns redacted previews, excludes common dirs",
16533
16533
  agents: ["architect", "reviewer", "critic_oversight"]
16534
16534
  },
16535
16535
  sast_scan: {
@@ -16537,7 +16537,7 @@ var init_tool_metadata = __esm(() => {
16537
16537
  agents: ["architect", "reviewer", "critic_oversight"]
16538
16538
  },
16539
16539
  build_check: {
16540
- description: "build verification",
16540
+ description: "discover and run build, typecheck, and test commands for various project ecosystems in the working directory",
16541
16541
  agents: ["architect", "coder", "test_engineer"]
16542
16542
  },
16543
16543
  pre_check_batch: {
@@ -16549,7 +16549,7 @@ var init_tool_metadata = __esm(() => {
16549
16549
  agents: ["architect"]
16550
16550
  },
16551
16551
  symbols: {
16552
- description: "code symbol search",
16552
+ description: "extract exported symbols (functions, classes, interfaces, types) from source files; supports TypeScript, JavaScript, and Python",
16553
16553
  agents: [
16554
16554
  "architect",
16555
16555
  "sme",
@@ -16620,7 +16620,7 @@ var init_tool_metadata = __esm(() => {
16620
16620
  agents: ["architect"]
16621
16621
  },
16622
16622
  checkpoint: {
16623
- description: "state snapshots",
16623
+ description: "create named git checkpoints for save, restore, and delete \u2014 use before risky operations to enable rollback",
16624
16624
  agents: ["architect"]
16625
16625
  },
16626
16626
  pkg_audit: {
@@ -16664,6 +16664,10 @@ var init_tool_metadata = __esm(() => {
16664
16664
  "explorer"
16665
16665
  ]
16666
16666
  },
16667
+ git_blame: {
16668
+ description: "per-line git blame metadata: sha, author, date, summary for each line in a file",
16669
+ agents: ["reviewer", "explorer", "architect"]
16670
+ },
16667
16671
  gitingest: {
16668
16672
  description: "fetch a GitHub repository full content via gitingest.com",
16669
16673
  agents: ["architect", "docs", "explorer"]
@@ -21701,8 +21705,9 @@ var init_guardrails = __esm(() => {
21701
21705
  function clearPendingCoderScope() {
21702
21706
  pendingCoderScopeByTaskId.clear();
21703
21707
  }
21704
- var pendingCoderScopeByTaskId, ACTIVE_PARALLEL_TASK_STATES;
21708
+ var EvidenceTaskIdPlanSchema, pendingCoderScopeByTaskId, ACTIVE_PARALLEL_TASK_STATES;
21705
21709
  var init_delegation_gate = __esm(() => {
21710
+ init_zod();
21706
21711
  init_schema();
21707
21712
  init_manager();
21708
21713
  init_state();
@@ -21712,6 +21717,14 @@ var init_delegation_gate = __esm(() => {
21712
21717
  init_guardrails();
21713
21718
  init_normalize_tool_name();
21714
21719
  init_utils2();
21720
+ EvidenceTaskIdPlanSchema = exports_external.object({
21721
+ phases: exports_external.array(exports_external.object({
21722
+ tasks: exports_external.array(exports_external.object({
21723
+ id: exports_external.string(),
21724
+ status: exports_external.string().optional()
21725
+ }).passthrough()).optional()
21726
+ }).passthrough()).optional()
21727
+ }).passthrough();
21715
21728
  pendingCoderScopeByTaskId = new Map;
21716
21729
  ACTIVE_PARALLEL_TASK_STATES = new Set([
21717
21730
  "coder_delegated",
@@ -51206,11 +51219,14 @@ async function defaultSelectTestFramework(profile, dir) {
51206
51219
  function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}) {
51207
51220
  const scope = opts.scope ?? "all";
51208
51221
  const coverage = opts.coverage ?? false;
51222
+ const bail = opts.bail ?? false;
51209
51223
  switch (framework) {
51210
51224
  case "bun": {
51211
51225
  const args = ["bun", "test"];
51212
51226
  if (coverage)
51213
51227
  args.push("--coverage");
51228
+ if (bail)
51229
+ args.push("--bail");
51214
51230
  if (scope !== "all" && files.length > 0)
51215
51231
  args.push(...files);
51216
51232
  return args;
@@ -51226,6 +51242,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51226
51242
  ];
51227
51243
  if (coverage)
51228
51244
  args.push("--coverage");
51245
+ if (bail)
51246
+ args.push("--bail");
51229
51247
  if (scope !== "all" && files.length > 0)
51230
51248
  args.push(...files);
51231
51249
  return args;
@@ -51234,12 +51252,16 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51234
51252
  const args = ["npx", "jest", "--json"];
51235
51253
  if (coverage)
51236
51254
  args.push("--coverage");
51255
+ if (bail)
51256
+ args.push("--bail");
51237
51257
  if (scope !== "all" && files.length > 0)
51238
51258
  args.push(...files);
51239
51259
  return args;
51240
51260
  }
51241
51261
  case "mocha": {
51242
51262
  const args = ["npx", "mocha"];
51263
+ if (bail)
51264
+ args.push("--bail");
51243
51265
  if (scope !== "all" && files.length > 0)
51244
51266
  args.push(...files);
51245
51267
  return args;
@@ -51249,6 +51271,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51249
51271
  const args = isWindows ? ["python", "-m", "pytest"] : ["python3", "-m", "pytest"];
51250
51272
  if (coverage)
51251
51273
  args.push("--cov=.", "--cov-report=term-missing");
51274
+ if (bail)
51275
+ args.push("-x");
51252
51276
  if (scope !== "all" && files.length > 0)
51253
51277
  args.push(...files);
51254
51278
  return args;
@@ -51302,6 +51326,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51302
51326
  return isCommandAvailable("flutter") ? ["flutter", "test", ...files] : ["dart", "test", ...files];
51303
51327
  case "rspec": {
51304
51328
  const args = isCommandAvailable("bundle") ? ["bundle", "exec", "rspec"] : ["rspec"];
51329
+ if (bail)
51330
+ args.push("--fail-fast");
51305
51331
  if (scope !== "all" && files.length > 0)
51306
51332
  args.push(...files);
51307
51333
  return args;
@@ -53276,6 +53302,10 @@ function validateArgs2(args) {
53276
53302
  if (typeof obj.coverage !== "boolean")
53277
53303
  return false;
53278
53304
  }
53305
+ if (obj.bail !== undefined) {
53306
+ if (typeof obj.bail !== "boolean")
53307
+ return false;
53308
+ }
53279
53309
  if (obj.timeout_ms !== undefined) {
53280
53310
  if (typeof obj.timeout_ms !== "number")
53281
53311
  return false;
@@ -53351,7 +53381,7 @@ async function detectTestFrameworkViaDispatch(cwd) {
53351
53381
  return "none";
53352
53382
  }
53353
53383
  }
53354
- async function buildTestCommandViaDispatch(framework, scope, files, coverage, baseDir) {
53384
+ async function buildTestCommandViaDispatch(framework, scope, files, coverage, baseDir, bail) {
53355
53385
  if (framework === "none")
53356
53386
  return null;
53357
53387
  try {
@@ -53360,7 +53390,8 @@ async function buildTestCommandViaDispatch(framework, scope, files, coverage, ba
53360
53390
  if (backend?.buildTestCommand) {
53361
53391
  const cmd = backend.buildTestCommand(framework, files, baseDir, {
53362
53392
  scope,
53363
- coverage
53393
+ coverage,
53394
+ bail
53364
53395
  });
53365
53396
  if (cmd)
53366
53397
  return cmd;
@@ -53729,12 +53760,14 @@ function getTargetedExecutionUnsupportedReason(framework) {
53729
53760
  return null;
53730
53761
  }
53731
53762
  }
53732
- function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53763
+ function buildTestCommand2(framework, scope, files, coverage, baseDir, bail) {
53733
53764
  switch (framework) {
53734
53765
  case "bun": {
53735
53766
  const args = ["bun", "test"];
53736
53767
  if (coverage)
53737
53768
  args.push("--coverage");
53769
+ if (bail)
53770
+ args.push("--bail");
53738
53771
  if (scope !== "all" && files.length > 0) {
53739
53772
  args.push(...files);
53740
53773
  }
@@ -53751,6 +53784,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53751
53784
  ];
53752
53785
  if (coverage)
53753
53786
  args.push("--coverage");
53787
+ if (bail)
53788
+ args.push("--bail=1");
53754
53789
  if (scope !== "all" && files.length > 0) {
53755
53790
  args.push(...files);
53756
53791
  }
@@ -53760,6 +53795,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53760
53795
  const args = ["npx", "jest", "--json"];
53761
53796
  if (coverage)
53762
53797
  args.push("--coverage");
53798
+ if (bail)
53799
+ args.push("--bail");
53763
53800
  if (scope !== "all" && files.length > 0) {
53764
53801
  args.push(...files);
53765
53802
  }
@@ -53767,6 +53804,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53767
53804
  }
53768
53805
  case "mocha": {
53769
53806
  const args = ["npx", "mocha"];
53807
+ if (bail)
53808
+ args.push("--bail");
53770
53809
  if (scope !== "all" && files.length > 0) {
53771
53810
  args.push(...files);
53772
53811
  }
@@ -53777,6 +53816,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53777
53816
  const args = isWindows ? ["python", "-m", "pytest"] : ["python3", "-m", "pytest"];
53778
53817
  if (coverage)
53779
53818
  args.push("--cov=.", "--cov-report=term-missing");
53819
+ if (bail)
53820
+ args.push("-x");
53780
53821
  if (scope !== "all" && files.length > 0) {
53781
53822
  args.push(...files);
53782
53823
  }
@@ -53833,6 +53874,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53833
53874
  return isCommandAvailable("flutter") ? ["flutter", "test", ...files] : ["dart", "test", ...files];
53834
53875
  case "rspec": {
53835
53876
  const args = isCommandAvailable("bundle") ? ["bundle", "exec", "rspec"] : ["rspec"];
53877
+ if (bail)
53878
+ args.push("--fail-fast");
53836
53879
  if (scope !== "all" && files.length > 0) {
53837
53880
  args.push(...files);
53838
53881
  }
@@ -54218,7 +54261,7 @@ async function readBoundedStream(stream, maxBytes) {
54218
54261
  }
54219
54262
  return { text: decoder.decode(combined), truncated };
54220
54263
  }
54221
- async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
54264
+ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail) {
54222
54265
  if (scope !== "all" && files.length > 0) {
54223
54266
  const unsupportedReason = getTargetedExecutionUnsupportedReason(framework);
54224
54267
  if (unsupportedReason) {
@@ -54233,7 +54276,7 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
54233
54276
  }
54234
54277
  }
54235
54278
  const useDispatchBuild = process.env.SWARM_LANG_BACKEND !== "legacy";
54236
- const command = useDispatchBuild ? await buildTestCommandViaDispatch(framework, scope, files, coverage, cwd) ?? buildTestCommand2(framework, scope, files, coverage, cwd) : buildTestCommand2(framework, scope, files, coverage, cwd);
54279
+ const command = useDispatchBuild ? await buildTestCommandViaDispatch(framework, scope, files, coverage, cwd, bail) ?? buildTestCommand2(framework, scope, files, coverage, cwd, bail) : buildTestCommand2(framework, scope, files, coverage, cwd, bail);
54237
54280
  if (!command) {
54238
54281
  return {
54239
54282
  success: false,
@@ -54581,6 +54624,7 @@ var init_test_runner = __esm(() => {
54581
54624
  scope: exports_external.enum(["all", "convention", "graph", "impact"]).optional().describe('Test scope: "all" runs full suite, "convention" accepts direct test files or maps source files to tests by naming, "graph" finds related tests via imports from source files, "impact" finds tests covering changed source files via test-impact analysis'),
54582
54625
  files: exports_external.array(exports_external.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
54583
54626
  coverage: exports_external.boolean().optional().describe("Enable coverage reporting if supported"),
54627
+ bail: exports_external.boolean().optional().describe("Stop running tests after the first failure. Default false. Note: coverage may be incomplete when bail=true with coverage=true."),
54584
54628
  timeout_ms: exports_external.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
54585
54629
  allow_full_suite: exports_external.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
54586
54630
  working_directory: exports_external.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
@@ -54681,6 +54725,7 @@ var init_test_runner = __esm(() => {
54681
54725
  }
54682
54726
  const _files = args.files || [];
54683
54727
  const coverage = args.coverage || false;
54728
+ const bail = args.bail || false;
54684
54729
  const timeout_ms = Math.min(args.timeout_ms || DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
54685
54730
  const useDispatch = process.env.SWARM_LANG_BACKEND !== "legacy";
54686
54731
  let framework;
@@ -54919,7 +54964,7 @@ var init_test_runner = __esm(() => {
54919
54964
  };
54920
54965
  return JSON.stringify(errorResult, null, 2);
54921
54966
  }
54922
- const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir);
54967
+ const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir, bail);
54923
54968
  recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined, result.testCases);
54924
54969
  let historyReport;
54925
54970
  if (!result.success && result.totals && result.totals.failed > 0) {
@@ -54935,6 +54980,9 @@ var init_test_runner = __esm(() => {
54935
54980
  if (graphFallbackReason && result.message) {
54936
54981
  result.message = `${result.message} (${graphFallbackReason})`;
54937
54982
  }
54983
+ if (bail && coverage && result.message) {
54984
+ result.message = `${result.message} (coverage may be incomplete: bail=true stopped early)`;
54985
+ }
54938
54986
  return JSON.stringify(result, null, 2);
54939
54987
  }
54940
54988
  });
@@ -55139,7 +55187,7 @@ async function runLintCheck(dir, linter, timeoutMs) {
55139
55187
  async function runTestsCheck(_dir, scope, timeoutMs) {
55140
55188
  const startTime = Date.now();
55141
55189
  try {
55142
- const result = await runTests("none", scope, [], false, timeoutMs, _dir);
55190
+ const result = await runTests("none", scope, [], false, timeoutMs, _dir, false);
55143
55191
  if (!result.success) {
55144
55192
  return {
55145
55193
  type: "tests",