opencode-swarm 7.50.4 → 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.4",
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"]
@@ -51215,11 +51219,14 @@ async function defaultSelectTestFramework(profile, dir) {
51215
51219
  function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}) {
51216
51220
  const scope = opts.scope ?? "all";
51217
51221
  const coverage = opts.coverage ?? false;
51222
+ const bail = opts.bail ?? false;
51218
51223
  switch (framework) {
51219
51224
  case "bun": {
51220
51225
  const args = ["bun", "test"];
51221
51226
  if (coverage)
51222
51227
  args.push("--coverage");
51228
+ if (bail)
51229
+ args.push("--bail");
51223
51230
  if (scope !== "all" && files.length > 0)
51224
51231
  args.push(...files);
51225
51232
  return args;
@@ -51235,6 +51242,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51235
51242
  ];
51236
51243
  if (coverage)
51237
51244
  args.push("--coverage");
51245
+ if (bail)
51246
+ args.push("--bail");
51238
51247
  if (scope !== "all" && files.length > 0)
51239
51248
  args.push(...files);
51240
51249
  return args;
@@ -51243,12 +51252,16 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51243
51252
  const args = ["npx", "jest", "--json"];
51244
51253
  if (coverage)
51245
51254
  args.push("--coverage");
51255
+ if (bail)
51256
+ args.push("--bail");
51246
51257
  if (scope !== "all" && files.length > 0)
51247
51258
  args.push(...files);
51248
51259
  return args;
51249
51260
  }
51250
51261
  case "mocha": {
51251
51262
  const args = ["npx", "mocha"];
51263
+ if (bail)
51264
+ args.push("--bail");
51252
51265
  if (scope !== "all" && files.length > 0)
51253
51266
  args.push(...files);
51254
51267
  return args;
@@ -51258,6 +51271,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51258
51271
  const args = isWindows ? ["python", "-m", "pytest"] : ["python3", "-m", "pytest"];
51259
51272
  if (coverage)
51260
51273
  args.push("--cov=.", "--cov-report=term-missing");
51274
+ if (bail)
51275
+ args.push("-x");
51261
51276
  if (scope !== "all" && files.length > 0)
51262
51277
  args.push(...files);
51263
51278
  return args;
@@ -51311,6 +51326,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
51311
51326
  return isCommandAvailable("flutter") ? ["flutter", "test", ...files] : ["dart", "test", ...files];
51312
51327
  case "rspec": {
51313
51328
  const args = isCommandAvailable("bundle") ? ["bundle", "exec", "rspec"] : ["rspec"];
51329
+ if (bail)
51330
+ args.push("--fail-fast");
51314
51331
  if (scope !== "all" && files.length > 0)
51315
51332
  args.push(...files);
51316
51333
  return args;
@@ -53285,6 +53302,10 @@ function validateArgs2(args) {
53285
53302
  if (typeof obj.coverage !== "boolean")
53286
53303
  return false;
53287
53304
  }
53305
+ if (obj.bail !== undefined) {
53306
+ if (typeof obj.bail !== "boolean")
53307
+ return false;
53308
+ }
53288
53309
  if (obj.timeout_ms !== undefined) {
53289
53310
  if (typeof obj.timeout_ms !== "number")
53290
53311
  return false;
@@ -53360,7 +53381,7 @@ async function detectTestFrameworkViaDispatch(cwd) {
53360
53381
  return "none";
53361
53382
  }
53362
53383
  }
53363
- async function buildTestCommandViaDispatch(framework, scope, files, coverage, baseDir) {
53384
+ async function buildTestCommandViaDispatch(framework, scope, files, coverage, baseDir, bail) {
53364
53385
  if (framework === "none")
53365
53386
  return null;
53366
53387
  try {
@@ -53369,7 +53390,8 @@ async function buildTestCommandViaDispatch(framework, scope, files, coverage, ba
53369
53390
  if (backend?.buildTestCommand) {
53370
53391
  const cmd = backend.buildTestCommand(framework, files, baseDir, {
53371
53392
  scope,
53372
- coverage
53393
+ coverage,
53394
+ bail
53373
53395
  });
53374
53396
  if (cmd)
53375
53397
  return cmd;
@@ -53738,12 +53760,14 @@ function getTargetedExecutionUnsupportedReason(framework) {
53738
53760
  return null;
53739
53761
  }
53740
53762
  }
53741
- function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53763
+ function buildTestCommand2(framework, scope, files, coverage, baseDir, bail) {
53742
53764
  switch (framework) {
53743
53765
  case "bun": {
53744
53766
  const args = ["bun", "test"];
53745
53767
  if (coverage)
53746
53768
  args.push("--coverage");
53769
+ if (bail)
53770
+ args.push("--bail");
53747
53771
  if (scope !== "all" && files.length > 0) {
53748
53772
  args.push(...files);
53749
53773
  }
@@ -53760,6 +53784,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53760
53784
  ];
53761
53785
  if (coverage)
53762
53786
  args.push("--coverage");
53787
+ if (bail)
53788
+ args.push("--bail=1");
53763
53789
  if (scope !== "all" && files.length > 0) {
53764
53790
  args.push(...files);
53765
53791
  }
@@ -53769,6 +53795,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53769
53795
  const args = ["npx", "jest", "--json"];
53770
53796
  if (coverage)
53771
53797
  args.push("--coverage");
53798
+ if (bail)
53799
+ args.push("--bail");
53772
53800
  if (scope !== "all" && files.length > 0) {
53773
53801
  args.push(...files);
53774
53802
  }
@@ -53776,6 +53804,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53776
53804
  }
53777
53805
  case "mocha": {
53778
53806
  const args = ["npx", "mocha"];
53807
+ if (bail)
53808
+ args.push("--bail");
53779
53809
  if (scope !== "all" && files.length > 0) {
53780
53810
  args.push(...files);
53781
53811
  }
@@ -53786,6 +53816,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53786
53816
  const args = isWindows ? ["python", "-m", "pytest"] : ["python3", "-m", "pytest"];
53787
53817
  if (coverage)
53788
53818
  args.push("--cov=.", "--cov-report=term-missing");
53819
+ if (bail)
53820
+ args.push("-x");
53789
53821
  if (scope !== "all" && files.length > 0) {
53790
53822
  args.push(...files);
53791
53823
  }
@@ -53842,6 +53874,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
53842
53874
  return isCommandAvailable("flutter") ? ["flutter", "test", ...files] : ["dart", "test", ...files];
53843
53875
  case "rspec": {
53844
53876
  const args = isCommandAvailable("bundle") ? ["bundle", "exec", "rspec"] : ["rspec"];
53877
+ if (bail)
53878
+ args.push("--fail-fast");
53845
53879
  if (scope !== "all" && files.length > 0) {
53846
53880
  args.push(...files);
53847
53881
  }
@@ -54227,7 +54261,7 @@ async function readBoundedStream(stream, maxBytes) {
54227
54261
  }
54228
54262
  return { text: decoder.decode(combined), truncated };
54229
54263
  }
54230
- async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
54264
+ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail) {
54231
54265
  if (scope !== "all" && files.length > 0) {
54232
54266
  const unsupportedReason = getTargetedExecutionUnsupportedReason(framework);
54233
54267
  if (unsupportedReason) {
@@ -54242,7 +54276,7 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
54242
54276
  }
54243
54277
  }
54244
54278
  const useDispatchBuild = process.env.SWARM_LANG_BACKEND !== "legacy";
54245
- 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);
54246
54280
  if (!command) {
54247
54281
  return {
54248
54282
  success: false,
@@ -54590,6 +54624,7 @@ var init_test_runner = __esm(() => {
54590
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'),
54591
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.'),
54592
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."),
54593
54628
  timeout_ms: exports_external.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
54594
54629
  allow_full_suite: exports_external.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
54595
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.")
@@ -54690,6 +54725,7 @@ var init_test_runner = __esm(() => {
54690
54725
  }
54691
54726
  const _files = args.files || [];
54692
54727
  const coverage = args.coverage || false;
54728
+ const bail = args.bail || false;
54693
54729
  const timeout_ms = Math.min(args.timeout_ms || DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
54694
54730
  const useDispatch = process.env.SWARM_LANG_BACKEND !== "legacy";
54695
54731
  let framework;
@@ -54928,7 +54964,7 @@ var init_test_runner = __esm(() => {
54928
54964
  };
54929
54965
  return JSON.stringify(errorResult, null, 2);
54930
54966
  }
54931
- const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir);
54967
+ const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir, bail);
54932
54968
  recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined, result.testCases);
54933
54969
  let historyReport;
54934
54970
  if (!result.success && result.totals && result.totals.failed > 0) {
@@ -54944,6 +54980,9 @@ var init_test_runner = __esm(() => {
54944
54980
  if (graphFallbackReason && result.message) {
54945
54981
  result.message = `${result.message} (${graphFallbackReason})`;
54946
54982
  }
54983
+ if (bail && coverage && result.message) {
54984
+ result.message = `${result.message} (coverage may be incomplete: bail=true stopped early)`;
54985
+ }
54947
54986
  return JSON.stringify(result, null, 2);
54948
54987
  }
54949
54988
  });
@@ -55148,7 +55187,7 @@ async function runLintCheck(dir, linter, timeoutMs) {
55148
55187
  async function runTestsCheck(_dir, scope, timeoutMs) {
55149
55188
  const startTime = Date.now();
55150
55189
  try {
55151
- const result = await runTests("none", scope, [], false, timeoutMs, _dir);
55190
+ const result = await runTests("none", scope, [], false, timeoutMs, _dir, false);
55152
55191
  if (!result.success) {
55153
55192
  return {
55154
55193
  type: "tests",