opencode-swarm 7.99.0 → 7.99.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
@@ -39,7 +39,7 @@ Most AI coding tools let one model write code and ask that same model whether th
39
39
  - 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion
40
40
  - 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
41
41
  - 🖥️ **PR Monitor** — GitHub PR subscription and background polling via `gh` CLI; delivers real-time CI, review, and merge status updates via the AutomationEventBus (FR-001, opt-in via `pr_monitor.enabled: true`). Subscribe with `/swarm pr subscribe <pr-url|owner/repo#N|N>`; unsubscribe with `/swarm pr unsubscribe <pr-url|owner/repo#N|N>`; check status with `/swarm pr status`. Enable `auto_pr_feedback: true` in `pr_monitor` config to inject `[MODE: PR_FEEDBACK pr="URL"]` on CI failures and merge conflicts automatically.
42
- - 🌐 **20 languages** TypeScript, Python, Go, Rust, Java, Kotlin, C/C++, C#, Ruby, Swift, Dart, PHP, JavaScript, CSS, Bash, PowerShell, INI, Regex (extending: see [docs/adding-a-language.md](docs/adding-a-language.md))
42
+ - 🌐 **12 full language profiles** (TypeScript, Python, Go, Rust, Java, Kotlin, C/C++, C#, Ruby, Swift, Dart, PHP) with **tree-sitter parse validation across 20 grammars** (adds JavaScript, CSS, Bash, PowerShell, INI, Regex — and `.tsx` / `.c` aliases) — extending: see [docs/adding-a-language.md](docs/adding-a-language.md)
43
43
  - 🛡️ **Built-in security** — SAST, secrets scanning, dependency audit per task
44
44
  - 🔒 **Scope enforcement** — Validates write targets against declared scope with cross-process persistence, TTL expiry, and scope-aware destructive command blocking. **Handles both single-string and array-based path arguments** (`files[]`, `paths[]`, `targetFiles[]`) to prevent scope bypass via multi-file tool calls.
45
45
  - 📝 **Shell write detection** — Static analysis of POSIX/PowerShell/cmd commands to detect file writes (redirects, builtins, in-place editors, network downloads, archive extraction, git destructive ops) before execution
@@ -522,6 +522,8 @@ Every agent runs inside a circuit breaker that prevents runaway behavior:
522
522
 
523
523
  Limits reset per task. Per-agent overrides available in config.
524
524
 
525
+ > **Note:** Built-in per-agent profiles override the global `max_consecutive_errors` to **8** for `architect`, `coder`, `test_engineer`, `explorer`, and `reviewer` (`src/config/schema.ts` `DEFAULT_AGENT_PROFILES`). The `5` shown above is the `GuardrailsConfigSchema` global default that applies only when no per-agent profile sets the value. See [docs/installation.md § Guardrails Configuration](docs/installation.md#guardrails-configuration) for the full schema and [docs/configuration.md § Per-agent override fields](docs/configuration.md#per-agent-override-fields) for how to override.
526
+
525
527
  ### File Authority (Per-Agent Write Permissions)
526
528
 
527
529
  Each agent can only write to specific paths:
@@ -540,7 +542,7 @@ Built-in tools verify every task before it ships:
540
542
 
541
543
  - **syntax_check** — Tree-sitter validation across the configured language grammar map
542
544
  - **placeholder_scan** — Catches TODOs, stubs, incomplete code
543
- - **sast_scan** — 63+ security rules, 9 languages (offline)
545
+ - **sast_scan** — 65 security rules across 7 languages (offline)
544
546
  - **sbom_generate** — Dependency tracking (CycloneDX)
545
547
  - **quality_budget** — Complexity, duplication, test ratio limits
546
548
 
@@ -796,7 +798,7 @@ Every candidate passes a 3-gate pipeline before entering quarantine:
796
798
  |------|-------------|
797
799
  | syntax_check | Tree-sitter validation across the configured language grammar map |
798
800
  | placeholder_scan | Catches TODOs, FIXMEs, stubs, placeholder text |
799
- | sast_scan | Offline security analysis, 63+ rules, 9 languages |
801
+ | sast_scan | Offline security analysis, 65 rules, 7 languages |
800
802
  | sbom_generate | CycloneDX dependency tracking, 8 ecosystems |
801
803
  | build_check | Runs your project's native build/typecheck |
802
804
  | incremental_verify | Post-coder hook for TS/JS, Go, Rust, Python, and C#; configured by `incremental_verify.*`, not invoked as a registered tool |
@@ -0,0 +1 @@
1
+ export declare function resolvePrompt(base: string, custom?: string, append?: string): string;
@@ -6,7 +6,7 @@ import {
6
6
  loadPlanJsonOnly,
7
7
  mergeDurableGateEntriesFromEvidence,
8
8
  readDurableGateEvidence
9
- } from "./index-wwnjw0fb.js";
9
+ } from "./index-qcr60fgm.js";
10
10
  import"./index-r8f89sm9.js";
11
11
  import"./index-9b7qp18e.js";
12
12
  import"./index-we94wkty.js";
@@ -6,7 +6,7 @@ import {
6
6
  CURATOR_POSTMORTEM_PROMPT,
7
7
  EXPLORER_PROMPT,
8
8
  createExplorerAgent
9
- } from "./index-2a6ppa65.js";
9
+ } from "./index-ydgy7vg5.js";
10
10
  import"./index-a76rekgs.js";
11
11
  export {
12
12
  createExplorerAgent,
@@ -1,15 +1,15 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-ft4gehk1.js";
5
- import"./index-hw8r3e5p.js";
4
+ } from "./index-sy329dh4.js";
5
+ import"./index-brpm7xs3.js";
6
6
  import"./index-xsswhy6k.js";
7
- import"./index-2a6ppa65.js";
7
+ import"./index-ydgy7vg5.js";
8
8
  import"./index-vwyjkzgs.js";
9
9
  import"./index-f7nma02k.js";
10
10
  import"./index-svf2zjxs.js";
11
11
  import"./index-7hnwnw5g.js";
12
- import"./index-wwnjw0fb.js";
12
+ import"./index-qcr60fgm.js";
13
13
  import"./index-adz3nk9b.js";
14
14
  import"./index-v4fcn4tr.js";
15
15
  import"./index-r8f89sm9.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  READ_ONLY_LANE_GUIDANCE
4
- } from "./index-2a6ppa65.js";
4
+ } from "./index-ydgy7vg5.js";
5
5
  import {
6
6
  DEFAULT_SKILL_MIN_CONFIDENCE,
7
7
  DEFAULT_SKILL_MIN_CONFIRMATIONS,
@@ -114,7 +114,7 @@ import {
114
114
  transientBackoff,
115
115
  validateProjectRoot,
116
116
  writeProjectedSpecSync
117
- } from "./index-wwnjw0fb.js";
117
+ } from "./index-qcr60fgm.js";
118
118
  import {
119
119
  _internals as _internals2,
120
120
  _internals1 as _internals3,
@@ -909,7 +909,7 @@ var init_executor = __esm(() => {
909
909
  // package.json
910
910
  var package_default = {
911
911
  name: "opencode-swarm",
912
- version: "7.99.0",
912
+ version: "7.99.1",
913
913
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
914
914
  main: "dist/index.js",
915
915
  types: "dist/index.d.ts",
@@ -3738,6 +3738,9 @@ function clearAllScopes(directory) {
3738
3738
  // src/hooks/shell-write-detect.ts
3739
3739
  import * as path8 from "path";
3740
3740
  import parse from "bash-parser";
3741
+ function buildDedupeKey(category, operator, path9) {
3742
+ return `${category}|${operator}|${path9 ?? "null"}`;
3743
+ }
3741
3744
  var REDIRECT_WRITE_TOKENS = new Set([
3742
3745
  "GREAT",
3743
3746
  "DGREAT",
@@ -4756,7 +4759,7 @@ function detectPosixWrites(command) {
4756
4759
  }
4757
4760
  const seen = new Set;
4758
4761
  const writes = allWrites.filter((wt) => {
4759
- const key = `${wt.category}|${wt.operator}|${wt.path ?? "null"}`;
4762
+ const key = buildDedupeKey(wt.category, wt.operator, wt.path);
4760
4763
  if (seen.has(key))
4761
4764
  return false;
4762
4765
  seen.add(key);
@@ -4781,7 +4784,7 @@ function detectWindowsWrites(command, shell) {
4781
4784
  }
4782
4785
  const seen = new Set;
4783
4786
  const writes = allWrites.filter((wt) => {
4784
- const key = `${wt.category}|${wt.operator}|${wt.path ?? "null"}`;
4787
+ const key = buildDedupeKey(wt.category, wt.operator, wt.path);
4785
4788
  if (seen.has(key))
4786
4789
  return false;
4787
4790
  seen.add(key);
@@ -5036,7 +5039,7 @@ function resolveWriteTargets(command, writes, cwd) {
5036
5039
  collectWritesWithNodes(ast, writesWithNodes, cwdStack);
5037
5040
  const seen = new Set;
5038
5041
  const deduplicated = writesWithNodes.filter((wwn) => {
5039
- const key = `${wwn.write.category}|${wwn.write.operator}|${wwn.write.path ?? "null"}`;
5042
+ const key = buildDedupeKey(wwn.write.category, wwn.write.operator, wwn.write.path);
5040
5043
  if (seen.has(key))
5041
5044
  return false;
5042
5045
  seen.add(key);
@@ -5046,11 +5049,11 @@ function resolveWriteTargets(command, writes, cwd) {
5046
5049
  for (const { write, context } of deduplicated) {
5047
5050
  const resolvedPath = resolvePath(write.path, context);
5048
5051
  const resolved = write.path !== null && !isDynamicPath(write.path);
5049
- const key = `${write.category}|${write.operator}|${write.path ?? "null"}`;
5052
+ const key = buildDedupeKey(write.category, write.operator, write.path);
5050
5053
  resolvedMap.set(key, { resolvedPath, resolved });
5051
5054
  }
5052
5055
  return writes.map((original) => {
5053
- const key = `${original.category}|${original.operator}|${original.path ?? "null"}`;
5056
+ const key = buildDedupeKey(original.category, original.operator, original.path);
5054
5057
  const resolved = resolvedMap.get(key);
5055
5058
  if (resolved) {
5056
5059
  return {
@@ -7650,7 +7653,7 @@ async function runCuratorPostMortem(directory, options = {}) {
7650
7653
  let reportContent;
7651
7654
  if (options.llmDelegate) {
7652
7655
  try {
7653
- const { CURATOR_POSTMORTEM_PROMPT: CURATOR_POSTMORTEM_PROMPT2 } = await import("./explorer-jc46negv.js");
7656
+ const { CURATOR_POSTMORTEM_PROMPT: CURATOR_POSTMORTEM_PROMPT2 } = await import("./explorer-rwfvbbx5.js");
7654
7657
  const userInput = assembleLLMInput(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
7655
7658
  const ac = new AbortController;
7656
7659
  const timer = setTimeout(() => ac.abort(), 300000);
@@ -17914,7 +17917,7 @@ async function handleEvidenceCommand(directory, args) {
17914
17917
  return formatTaskEvidenceMarkdown(evidenceData);
17915
17918
  }
17916
17919
  async function handleEvidenceSummaryCommand(directory) {
17917
- const { buildEvidenceSummary } = await import("./evidence-summary-service-4hs44n2c.js");
17920
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-gvd7mw7v.js");
17918
17921
  const artifact = await buildEvidenceSummary(directory);
17919
17922
  if (!artifact) {
17920
17923
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -19424,12 +19427,50 @@ var KNOWLEDGE_SCHEMA_VERSION = 2;
19424
19427
 
19425
19428
  // src/hooks/knowledge-migrator.ts
19426
19429
  async function migrateKnowledgeToExternal(_directory, _config) {
19430
+ const externalSentinelPath = path39.join(_directory, ".swarm", ".knowledge-external-migrated");
19431
+ const contextPath = path39.join(_directory, ".swarm", "context.md");
19432
+ if (_internals30.existsSync(externalSentinelPath)) {
19433
+ return {
19434
+ migrated: false,
19435
+ entriesMigrated: 0,
19436
+ entriesDropped: 0,
19437
+ entriesTotal: 0,
19438
+ skippedReason: "external-sentinel-exists"
19439
+ };
19440
+ }
19441
+ if (!_internals30.existsSync(contextPath)) {
19442
+ return {
19443
+ migrated: false,
19444
+ entriesMigrated: 0,
19445
+ entriesDropped: 0,
19446
+ entriesTotal: 0,
19447
+ skippedReason: "no-context-file"
19448
+ };
19449
+ }
19450
+ const contextContent = await _internals30.readFile(contextPath, "utf-8");
19451
+ if (contextContent.trim().length === 0) {
19452
+ return {
19453
+ migrated: false,
19454
+ entriesMigrated: 0,
19455
+ entriesDropped: 0,
19456
+ entriesTotal: 0,
19457
+ skippedReason: "empty-context"
19458
+ };
19459
+ }
19460
+ const lines = contextContent.split(`
19461
+ `);
19462
+ let entriesCount = 0;
19463
+ for (const line of lines) {
19464
+ if (line.trimStart().startsWith("- ")) {
19465
+ entriesCount++;
19466
+ }
19467
+ }
19468
+ await _internals30.writeSentinel(externalSentinelPath, entriesCount, entriesCount);
19427
19469
  return {
19428
- migrated: false,
19429
- entriesMigrated: 0,
19470
+ migrated: true,
19471
+ entriesMigrated: entriesCount,
19430
19472
  entriesDropped: 0,
19431
- entriesTotal: 0,
19432
- skippedReason: "no-context-file"
19473
+ entriesTotal: entriesCount
19433
19474
  };
19434
19475
  }
19435
19476
  var _internals30 = {
@@ -19444,7 +19485,12 @@ var _internals30 = {
19444
19485
  truncateLesson: truncateLesson2,
19445
19486
  inferProjectName,
19446
19487
  writeSentinel,
19447
- resolveLegacyHiveKnowledgePath
19488
+ resolveLegacyHiveKnowledgePath,
19489
+ existsSync: existsSync23,
19490
+ readFileSync: readFileSync15,
19491
+ readFile: readFile11,
19492
+ mkdir: mkdir9,
19493
+ writeFile: writeFile9
19448
19494
  };
19449
19495
  async function migrateContextToKnowledge(directory, config) {
19450
19496
  const sentinelPath = path39.join(directory, ".swarm", ".knowledge-migrated");
@@ -19782,8 +19828,8 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
19782
19828
  schema_version: 1,
19783
19829
  migration_tool: "knowledge-migrator.ts"
19784
19830
  };
19785
- await mkdir9(path39.dirname(sentinelPath), { recursive: true });
19786
- await writeFile9(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
19831
+ await _internals30.mkdir(path39.dirname(sentinelPath), { recursive: true });
19832
+ await _internals30.writeFile(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
19787
19833
  }
19788
19834
  function resolveLegacyHiveKnowledgePath() {
19789
19835
  const platform = process.platform;
@@ -32165,7 +32211,7 @@ function buildDetailedHelp(commandName, entry) {
32165
32211
  async function handleHelpCommand(ctx) {
32166
32212
  const targetCommand = ctx.args.join(" ");
32167
32213
  if (!targetCommand) {
32168
- const { buildHelpText } = await import("./index-4pt2py5p.js");
32214
+ const { buildHelpText } = await import("./index-xy3gw0rw.js");
32169
32215
  return buildHelpText();
32170
32216
  }
32171
32217
  const tokens = targetCommand.split(/\s+/);
@@ -32174,7 +32220,7 @@ async function handleHelpCommand(ctx) {
32174
32220
  return _internals45.buildDetailedHelp(resolved.key, resolved.entry);
32175
32221
  }
32176
32222
  const similar = _internals45.findSimilarCommands(targetCommand);
32177
- const { buildHelpText: fullHelp } = await import("./index-4pt2py5p.js");
32223
+ const { buildHelpText: fullHelp } = await import("./index-xy3gw0rw.js");
32178
32224
  if (similar.length > 0) {
32179
32225
  return `Command '/swarm ${targetCommand}' not found.
32180
32226
 
@@ -32307,7 +32353,7 @@ var COMMAND_REGISTRY = {
32307
32353
  },
32308
32354
  "guardrail explain": {
32309
32355
  handler: async (ctx) => {
32310
- const { handleGuardrailExplain } = await import("./guardrail-explain-ktm6szbm.js");
32356
+ const { handleGuardrailExplain } = await import("./guardrail-explain-6g1japmf.js");
32311
32357
  return handleGuardrailExplain(ctx.directory, ctx.args);
32312
32358
  },
32313
32359
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -939,6 +939,26 @@ var _internals2 = {
939
939
  // src/turbo/lean/conflicts.ts
940
940
  import * as fs3 from "fs";
941
941
  import * as path3 from "path";
942
+
943
+ // src/utils/path.ts
944
+ function normalizePath(p) {
945
+ if (!p)
946
+ return "";
947
+ let result = p.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "").replace(/(?:^|\/)\.\//g, "/");
948
+ result = result.replace(/\/$/, "");
949
+ result = result.replace(/\/\.$/, "");
950
+ if (process.platform === "win32")
951
+ result = result.toLowerCase();
952
+ if (!result) {
953
+ const norm = p.replace(/\\/g, "/");
954
+ if (norm === "." || norm === "./")
955
+ return ".";
956
+ return "";
957
+ }
958
+ return result;
959
+ }
960
+
961
+ // src/turbo/lean/conflicts.ts
942
962
  var DEFAULT_GLOBAL_FILES = [
943
963
  "package.json",
944
964
  "package-lock.json",
@@ -997,18 +1017,6 @@ var BARREL_FILE_PATTERNS = [
997
1017
  /\/exports\.ts$/,
998
1018
  /\/types\.ts$/
999
1019
  ];
1000
- function normalizePath(filePath) {
1001
- if (filePath === "." || filePath === "./") {
1002
- return ".";
1003
- }
1004
- let result = filePath.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "").replace(/(?:^|\/)\.\//g, "/");
1005
- result = result.replace(/\/$/, "");
1006
- result = result.replace(/\/\.$/, "");
1007
- if (process.platform === "win32") {
1008
- result = result.toLowerCase();
1009
- }
1010
- return result;
1011
- }
1012
1020
  function pathsConflict(path1, path22) {
1013
1021
  if (process.platform === "win32") {
1014
1022
  path1 = path1.toLowerCase();
@@ -3535,6 +3543,7 @@ async function saveEvidence(directory, taskId, evidence) {
3535
3543
  const sanitizedTaskId = sanitizeTaskId2(taskId);
3536
3544
  const relativePath = path7.join("evidence", sanitizedTaskId, "evidence.json");
3537
3545
  validateSwarmPath(directory, relativePath);
3546
+ _internals5.validateEvidence(evidence);
3538
3547
  return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
3539
3548
  const evidencePath = validateSwarmPath(directory, relativePath);
3540
3549
  const evidenceDir = path7.dirname(evidencePath);
@@ -3771,11 +3780,15 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
3771
3780
  }
3772
3781
  return archived;
3773
3782
  }
3783
+ function validateEvidence(evidence) {
3784
+ return EvidenceSchema.parse(evidence);
3785
+ }
3774
3786
  var _internals5 = {
3775
3787
  wrapFlatRetrospective,
3776
3788
  loadEvidence,
3777
3789
  listEvidenceTaskIds,
3778
- validateProjectRoot
3790
+ validateProjectRoot,
3791
+ validateEvidence
3779
3792
  };
3780
3793
 
3781
3794
  // src/evidence/gate-bridge.ts
@@ -12,7 +12,7 @@ import {
12
12
  detectPosixWrites,
13
13
  detectWindowsWrites,
14
14
  resolveWriteTargets
15
- } from "./index-hw8r3e5p.js";
15
+ } from "./index-brpm7xs3.js";
16
16
  import {
17
17
  checkFileAuthority,
18
18
  classifyFile,
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-ft4gehk1.js";
4
+ } from "./index-sy329dh4.js";
5
5
  import {
6
6
  handleGuardrailLog
7
7
  } from "./index-h9ptj4b1.js";
@@ -79,9 +79,9 @@ import {
79
79
  handleWriteRetroCommand,
80
80
  normalizeSwarmCommandInput,
81
81
  resolveCommand
82
- } from "./index-hw8r3e5p.js";
82
+ } from "./index-brpm7xs3.js";
83
83
  import"./index-xsswhy6k.js";
84
- import"./index-2a6ppa65.js";
84
+ import"./index-ydgy7vg5.js";
85
85
  import"./index-vwyjkzgs.js";
86
86
  import"./index-f7nma02k.js";
87
87
  import"./index-svf2zjxs.js";
@@ -90,7 +90,7 @@ import {
90
90
  ORCHESTRATOR_NAME,
91
91
  stripKnownSwarmPrefix
92
92
  } from "./index-7hnwnw5g.js";
93
- import"./index-wwnjw0fb.js";
93
+ import"./index-qcr60fgm.js";
94
94
  import"./index-adz3nk9b.js";
95
95
  import"./index-v4fcn4tr.js";
96
96
  import"./index-r8f89sm9.js";
@@ -1,4 +1,15 @@
1
1
  // @bun
2
+ // src/agents/_prompt-helpers.ts
3
+ function resolvePrompt(base, custom, append) {
4
+ if (custom)
5
+ return custom;
6
+ if (append)
7
+ return `${base}
8
+
9
+ ${append}`;
10
+ return base;
11
+ }
12
+
2
13
  // src/agents/read-only-lane-guidance.ts
3
14
  var READ_ONLY_LANE_GUIDANCE = `## READ-ONLY ADVISORY LANE CONTEXT
4
15
 
@@ -379,13 +390,7 @@ OUTPUT FORMAT:
379
390
  `;
380
391
  function createExplorerAgent(model, customPrompt, customAppendPrompt) {
381
392
  let prompt = EXPLORER_PROMPT;
382
- if (customPrompt) {
383
- prompt = customPrompt;
384
- } else if (customAppendPrompt) {
385
- prompt = `${EXPLORER_PROMPT}
386
-
387
- ${customAppendPrompt}`;
388
- }
393
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
389
394
  return {
390
395
  name: "explorer",
391
396
  description: "Fast codebase discovery and analysis. Scans directory structure, identifies languages/frameworks, summarizes key files, and identifies areas where specialized domain knowledge may be beneficial.",
package/dist/cli/index.js CHANGED
@@ -7,16 +7,16 @@ import {
7
7
  getPluginLockFilePaths,
8
8
  package_default,
9
9
  resolveCommand
10
- } from "./index-hw8r3e5p.js";
10
+ } from "./index-brpm7xs3.js";
11
11
  import"./index-xsswhy6k.js";
12
- import"./index-2a6ppa65.js";
12
+ import"./index-ydgy7vg5.js";
13
13
  import"./index-vwyjkzgs.js";
14
14
  import"./index-f7nma02k.js";
15
15
  import"./index-svf2zjxs.js";
16
16
  import {
17
17
  DEFAULT_AGENT_CONFIGS
18
18
  } from "./index-7hnwnw5g.js";
19
- import"./index-wwnjw0fb.js";
19
+ import"./index-qcr60fgm.js";
20
20
  import"./index-adz3nk9b.js";
21
21
  import"./index-v4fcn4tr.js";
22
22
  import"./index-r8f89sm9.js";
@@ -93,6 +93,10 @@ export declare function checkRequirementCoverage(phase: number, directory: strin
93
93
  * Returns array of archived (deleted) task IDs.
94
94
  */
95
95
  export declare function archiveEvidence(directory: string, maxAgeDays: number, maxBundles?: number): Promise<string[]>;
96
+ /**
97
+ * Validates evidence through Zod. Exposed via _internals for DI testing.
98
+ */
99
+ declare function validateEvidence(evidence: Evidence): Evidence;
96
100
  /**
97
101
  * DI seam for testability. Contains all test-mocked exports.
98
102
  * Internal calls should use _internals.fn() instead of fn() directly.
@@ -102,5 +106,6 @@ export declare const _internals: {
102
106
  loadEvidence: typeof loadEvidence;
103
107
  listEvidenceTaskIds: typeof listEvidenceTaskIds;
104
108
  validateProjectRoot: typeof validateProjectRoot;
109
+ validateEvidence: typeof validateEvidence;
105
110
  };
106
111
  export {};
@@ -1,4 +1,6 @@
1
1
  /** One-time migration from .swarm/context.md → .swarm/knowledge.jsonl for existing projects. */
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
4
  import { appendKnowledge } from './knowledge-store.js';
3
5
  import type { KnowledgeCategory, KnowledgeConfig } from './knowledge-types.js';
4
6
  export interface MigrationResult {
@@ -32,6 +34,11 @@ export declare const _internals: {
32
34
  inferProjectName: typeof inferProjectName;
33
35
  writeSentinel: typeof writeSentinel;
34
36
  resolveLegacyHiveKnowledgePath: typeof resolveLegacyHiveKnowledgePath;
37
+ existsSync: typeof existsSync;
38
+ readFileSync: typeof readFileSync;
39
+ readFile: typeof readFile;
40
+ mkdir: typeof mkdir;
41
+ writeFile: typeof writeFile;
35
42
  };
36
43
  export declare function migrateContextToKnowledge(directory: string, config: KnowledgeConfig): Promise<MigrationResult>;
37
44
  /**
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var package_default;
69
69
  var init_package = __esm(() => {
70
70
  package_default = {
71
71
  name: "opencode-swarm",
72
- version: "7.99.0",
72
+ version: "7.99.1",
73
73
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
74
74
  main: "dist/index.js",
75
75
  types: "dist/index.d.ts",
@@ -17329,6 +17329,17 @@ var init_warning_buffer = __esm(() => {
17329
17329
  deferredWarnings = [];
17330
17330
  });
17331
17331
 
17332
+ // src/agents/_prompt-helpers.ts
17333
+ function resolvePrompt(base, custom2, append) {
17334
+ if (custom2)
17335
+ return custom2;
17336
+ if (append)
17337
+ return `${base}
17338
+
17339
+ ${append}`;
17340
+ return base;
17341
+ }
17342
+
17332
17343
  // src/config/bundled-skills.ts
17333
17344
  import * as fs2 from "node:fs";
17334
17345
  import * as fsp from "node:fs/promises";
@@ -19371,21 +19382,27 @@ var init_task_commit = __esm(() => {
19371
19382
  };
19372
19383
  });
19373
19384
 
19374
- // src/turbo/lean/conflicts.ts
19375
- import * as fs7 from "node:fs";
19376
- import * as path10 from "node:path";
19377
- function normalizePath(filePath) {
19378
- if (filePath === "." || filePath === "./") {
19379
- return ".";
19380
- }
19381
- let result = filePath.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "").replace(/(?:^|\/)\.\//g, "/");
19385
+ // src/utils/path.ts
19386
+ function normalizePath(p) {
19387
+ if (!p)
19388
+ return "";
19389
+ let result = p.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "").replace(/(?:^|\/)\.\//g, "/");
19382
19390
  result = result.replace(/\/$/, "");
19383
19391
  result = result.replace(/\/\.$/, "");
19384
- if (process.platform === "win32") {
19392
+ if (process.platform === "win32")
19385
19393
  result = result.toLowerCase();
19394
+ if (!result) {
19395
+ const norm = p.replace(/\\/g, "/");
19396
+ if (norm === "." || norm === "./")
19397
+ return ".";
19398
+ return "";
19386
19399
  }
19387
19400
  return result;
19388
19401
  }
19402
+
19403
+ // src/turbo/lean/conflicts.ts
19404
+ import * as fs7 from "node:fs";
19405
+ import * as path10 from "node:path";
19389
19406
  function isPathSafe(filePath) {
19390
19407
  const normalized = normalizePath(filePath);
19391
19408
  if (normalized.includes("..")) {
@@ -23739,6 +23756,7 @@ async function saveEvidence(directory, taskId, evidence) {
23739
23756
  const sanitizedTaskId = sanitizeTaskId2(taskId);
23740
23757
  const relativePath = path15.join("evidence", sanitizedTaskId, "evidence.json");
23741
23758
  validateSwarmPath(directory, relativePath);
23759
+ _internals10.validateEvidence(evidence);
23742
23760
  return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
23743
23761
  const evidencePath = validateSwarmPath(directory, relativePath);
23744
23762
  const evidenceDir = path15.dirname(evidencePath);
@@ -23970,6 +23988,9 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
23970
23988
  }
23971
23989
  return archived;
23972
23990
  }
23991
+ function validateEvidence(evidence) {
23992
+ return EvidenceSchema.parse(evidence);
23993
+ }
23973
23994
  var VALID_EVIDENCE_TYPES, sanitizeTaskId2, MAX_DEPTH = 20, PROJECT_INDICATORS, LEGACY_TASK_COMPLEXITY_MAP, _internals10;
23974
23995
  var init_manager2 = __esm(() => {
23975
23996
  init_zod();
@@ -24017,7 +24038,8 @@ var init_manager2 = __esm(() => {
24017
24038
  wrapFlatRetrospective,
24018
24039
  loadEvidence,
24019
24040
  listEvidenceTaskIds,
24020
- validateProjectRoot
24041
+ validateProjectRoot,
24042
+ validateEvidence
24021
24043
  };
24022
24044
  });
24023
24045
 
@@ -41214,6 +41236,9 @@ var require_src = __commonJS((exports, module) => {
41214
41236
 
41215
41237
  // src/hooks/shell-write-detect.ts
41216
41238
  import * as path25 from "node:path";
41239
+ function buildDedupeKey(category, operator, path26) {
41240
+ return `${category}|${operator}|${path26 ?? "null"}`;
41241
+ }
41217
41242
  function detectProcessSubstitutionWrites(command) {
41218
41243
  const results = [];
41219
41244
  const writeProcSubPattern = />\s*\(([^)]+)\)/g;
@@ -42188,7 +42213,7 @@ function detectPosixWrites(command) {
42188
42213
  }
42189
42214
  const seen = new Set;
42190
42215
  const writes = allWrites.filter((wt) => {
42191
- const key = `${wt.category}|${wt.operator}|${wt.path ?? "null"}`;
42216
+ const key = buildDedupeKey(wt.category, wt.operator, wt.path);
42192
42217
  if (seen.has(key))
42193
42218
  return false;
42194
42219
  seen.add(key);
@@ -42213,7 +42238,7 @@ function detectWindowsWrites(command, shell) {
42213
42238
  }
42214
42239
  const seen = new Set;
42215
42240
  const writes = allWrites.filter((wt) => {
42216
- const key = `${wt.category}|${wt.operator}|${wt.path ?? "null"}`;
42241
+ const key = buildDedupeKey(wt.category, wt.operator, wt.path);
42217
42242
  if (seen.has(key))
42218
42243
  return false;
42219
42244
  seen.add(key);
@@ -42468,7 +42493,7 @@ function resolveWriteTargets(command, writes, cwd) {
42468
42493
  collectWritesWithNodes(ast, writesWithNodes, cwdStack);
42469
42494
  const seen = new Set;
42470
42495
  const deduplicated = writesWithNodes.filter((wwn) => {
42471
- const key = `${wwn.write.category}|${wwn.write.operator}|${wwn.write.path ?? "null"}`;
42496
+ const key = buildDedupeKey(wwn.write.category, wwn.write.operator, wwn.write.path);
42472
42497
  if (seen.has(key))
42473
42498
  return false;
42474
42499
  seen.add(key);
@@ -42478,11 +42503,11 @@ function resolveWriteTargets(command, writes, cwd) {
42478
42503
  for (const { write, context } of deduplicated) {
42479
42504
  const resolvedPath = resolvePath(write.path, context);
42480
42505
  const resolved = write.path !== null && !isDynamicPath(write.path);
42481
- const key = `${write.category}|${write.operator}|${write.path ?? "null"}`;
42506
+ const key = buildDedupeKey(write.category, write.operator, write.path);
42482
42507
  resolvedMap.set(key, { resolvedPath, resolved });
42483
42508
  }
42484
42509
  return writes.map((original) => {
42485
- const key = `${original.category}|${original.operator}|${original.path ?? "null"}`;
42510
+ const key = buildDedupeKey(original.category, original.operator, original.path);
42486
42511
  const resolved = resolvedMap.get(key);
42487
42512
  if (resolved) {
42488
42513
  return {
@@ -63255,13 +63280,7 @@ __export(exports_explorer, {
63255
63280
  });
63256
63281
  function createExplorerAgent(model, customPrompt, customAppendPrompt) {
63257
63282
  let prompt = EXPLORER_PROMPT;
63258
- if (customPrompt) {
63259
- prompt = customPrompt;
63260
- } else if (customAppendPrompt) {
63261
- prompt = `${EXPLORER_PROMPT}
63262
-
63263
- ${customAppendPrompt}`;
63264
- }
63283
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
63265
63284
  return {
63266
63285
  name: "explorer",
63267
63286
  description: "Fast codebase discovery and analysis. Scans directory structure, identifies languages/frameworks, summarizes key files, and identifies areas where specialized domain knowledge may be beneficial.",
@@ -83526,12 +83545,50 @@ import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile15 } fr
83526
83545
  import * as os16 from "node:os";
83527
83546
  import * as path76 from "node:path";
83528
83547
  async function migrateKnowledgeToExternal(_directory, _config) {
83548
+ const externalSentinelPath = path76.join(_directory, ".swarm", ".knowledge-external-migrated");
83549
+ const contextPath = path76.join(_directory, ".swarm", "context.md");
83550
+ if (_internals51.existsSync(externalSentinelPath)) {
83551
+ return {
83552
+ migrated: false,
83553
+ entriesMigrated: 0,
83554
+ entriesDropped: 0,
83555
+ entriesTotal: 0,
83556
+ skippedReason: "external-sentinel-exists"
83557
+ };
83558
+ }
83559
+ if (!_internals51.existsSync(contextPath)) {
83560
+ return {
83561
+ migrated: false,
83562
+ entriesMigrated: 0,
83563
+ entriesDropped: 0,
83564
+ entriesTotal: 0,
83565
+ skippedReason: "no-context-file"
83566
+ };
83567
+ }
83568
+ const contextContent = await _internals51.readFile(contextPath, "utf-8");
83569
+ if (contextContent.trim().length === 0) {
83570
+ return {
83571
+ migrated: false,
83572
+ entriesMigrated: 0,
83573
+ entriesDropped: 0,
83574
+ entriesTotal: 0,
83575
+ skippedReason: "empty-context"
83576
+ };
83577
+ }
83578
+ const lines = contextContent.split(`
83579
+ `);
83580
+ let entriesCount = 0;
83581
+ for (const line of lines) {
83582
+ if (line.trimStart().startsWith("- ")) {
83583
+ entriesCount++;
83584
+ }
83585
+ }
83586
+ await _internals51.writeSentinel(externalSentinelPath, entriesCount, entriesCount);
83529
83587
  return {
83530
- migrated: false,
83531
- entriesMigrated: 0,
83588
+ migrated: true,
83589
+ entriesMigrated: entriesCount,
83532
83590
  entriesDropped: 0,
83533
- entriesTotal: 0,
83534
- skippedReason: "no-context-file"
83591
+ entriesTotal: entriesCount
83535
83592
  };
83536
83593
  }
83537
83594
  async function migrateContextToKnowledge(directory, config3) {
@@ -83870,8 +83927,8 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
83870
83927
  schema_version: 1,
83871
83928
  migration_tool: "knowledge-migrator.ts"
83872
83929
  };
83873
- await mkdir17(path76.dirname(sentinelPath), { recursive: true });
83874
- await writeFile15(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
83930
+ await _internals51.mkdir(path76.dirname(sentinelPath), { recursive: true });
83931
+ await _internals51.writeFile(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
83875
83932
  }
83876
83933
  function resolveLegacyHiveKnowledgePath() {
83877
83934
  const platform = process.platform;
@@ -83903,7 +83960,12 @@ var init_knowledge_migrator = __esm(() => {
83903
83960
  truncateLesson: truncateLesson2,
83904
83961
  inferProjectName,
83905
83962
  writeSentinel,
83906
- resolveLegacyHiveKnowledgePath
83963
+ resolveLegacyHiveKnowledgePath,
83964
+ existsSync: existsSync43,
83965
+ readFileSync: readFileSync27,
83966
+ readFile: readFile19,
83967
+ mkdir: mkdir17,
83968
+ writeFile: writeFile15
83907
83969
  };
83908
83970
  });
83909
83971
 
@@ -102308,13 +102370,7 @@ function buildSlashCommandsList() {
102308
102370
  }
102309
102371
  function createArchitectAgent(model, customPrompt, customAppendPrompt, adversarialTesting, council, uiReview, memoryEnabled = false, architecturalSupervision, designDocsEnabled = false, externalSkillsEnabled = false, turboEnabled = false, skillsEnabled = false) {
102310
102372
  let prompt = ARCHITECT_PROMPT;
102311
- if (customPrompt) {
102312
- prompt = customPrompt;
102313
- } else if (customAppendPrompt) {
102314
- prompt = `${ARCHITECT_PROMPT}
102315
-
102316
- ${customAppendPrompt}`;
102317
- }
102373
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
102318
102374
  prompt = prompt?.replace("{{YOUR_TOOLS}}", buildYourToolsList(council, memoryEnabled, externalSkillsEnabled, turboEnabled, skillsEnabled))?.replace("{{AVAILABLE_TOOLS}}", buildAvailableToolsList(council, memoryEnabled, externalSkillsEnabled, turboEnabled, skillsEnabled))?.replace("{{SLASH_COMMANDS}}", buildSlashCommandsList());
102319
102375
  prompt = prompt?.replace(/\{\{QA_GATE_DIALOGUE_SPECIFY\}\}/g, buildQaGateSelectionDialogue("SPECIFY"))?.replace(/\{\{QA_GATE_DIALOGUE_BRAINSTORM\}\}/g, buildQaGateSelectionDialogue("BRAINSTORM"))?.replace(/\{\{QA_GATE_DIALOGUE_PLAN\}\}/g, buildQaGateSelectionDialogue("PLAN"));
102320
102376
  const councilBlock = buildCouncilWorkflow(council);
@@ -103370,13 +103426,7 @@ var init_architect = __esm(() => {
103370
103426
  // src/agents/coder.ts
103371
103427
  function createCoderAgent(model, customPrompt, customAppendPrompt) {
103372
103428
  let prompt = CODER_PROMPT;
103373
- if (customPrompt) {
103374
- prompt = customPrompt;
103375
- } else if (customAppendPrompt) {
103376
- prompt = `${CODER_PROMPT}
103377
-
103378
- ${customAppendPrompt}`;
103379
- }
103429
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
103380
103430
  return {
103381
103431
  name: "coder",
103382
103432
  description: "Production-quality code implementation specialist. Receives unified specifications and writes complete, working code.",
@@ -103626,6 +103676,7 @@ META.SUMMARY CONVENTION — When reporting task completion, include:
103626
103676
  Write for the next agent reading the event log, not for a human.
103627
103677
 
103628
103678
  `;
103679
+ var init_coder = () => {};
103629
103680
 
103630
103681
  // src/agents/council-prompts.ts
103631
103682
  var ROUND_PROTOCOL = `================================================================
@@ -104485,13 +104536,7 @@ var init_curator_agent = __esm(() => {
104485
104536
  // src/agents/designer.ts
104486
104537
  function createDesignerAgent(model, customPrompt, customAppendPrompt) {
104487
104538
  let prompt = DESIGNER_PROMPT;
104488
- if (customPrompt) {
104489
- prompt = customPrompt;
104490
- } else if (customAppendPrompt) {
104491
- prompt = `${DESIGNER_PROMPT}
104492
-
104493
- ${customAppendPrompt}`;
104494
- }
104539
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
104495
104540
  return {
104496
104541
  name: "designer",
104497
104542
  description: "UI/UX design specification agent. Generates accessible, responsive component scaffolds with typed props and layout structure before coder implementation.",
@@ -104666,18 +104711,13 @@ RULES:
104666
104711
  - Keep output under 3000 characters per component
104667
104712
 
104668
104713
  `;
104714
+ var init_designer = () => {};
104669
104715
 
104670
104716
  // src/agents/docs.ts
104671
104717
  function createDocsAgent(model, customPrompt, customAppendPrompt, role = "standard") {
104672
104718
  const roleConfig = ROLE_CONFIG2[role];
104673
104719
  let prompt = roleConfig.basePrompt;
104674
- if (customPrompt) {
104675
- prompt = customPrompt;
104676
- } else if (customAppendPrompt) {
104677
- prompt = `${roleConfig.basePrompt}
104678
-
104679
- ${customAppendPrompt}`;
104680
- }
104720
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
104681
104721
  return {
104682
104722
  name: roleConfig.name,
104683
104723
  description: roleConfig.description,
@@ -104880,13 +104920,7 @@ var init_docs = __esm(() => {
104880
104920
  // src/agents/researcher.ts
104881
104921
  function createResearcherAgent(model, customPrompt, customAppendPrompt) {
104882
104922
  let prompt = RESEARCHER_PROMPT;
104883
- if (customPrompt) {
104884
- prompt = customPrompt;
104885
- } else if (customAppendPrompt) {
104886
- prompt = `${RESEARCHER_PROMPT}
104887
-
104888
- ${customAppendPrompt}`;
104889
- }
104923
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
104890
104924
  return {
104891
104925
  name: "researcher",
104892
104926
  description: "Automated multi-source research specialist. Searches the web, GitHub, official docs, and academic sources, then synthesises findings with citations. Read-only.",
@@ -105036,13 +105070,7 @@ Do not pad responses with hedging when confidence is HIGH. A precise answer is m
105036
105070
  // src/agents/reviewer.ts
105037
105071
  function createReviewerAgent(model, customPrompt, customAppendPrompt) {
105038
105072
  let prompt = REVIEWER_PROMPT;
105039
- if (customPrompt) {
105040
- prompt = customPrompt;
105041
- } else if (customAppendPrompt) {
105042
- prompt = `${REVIEWER_PROMPT}
105043
-
105044
- ${customAppendPrompt}`;
105045
- }
105073
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
105046
105074
  return {
105047
105075
  name: "reviewer",
105048
105076
  description: "Code reviewer. Verifies correctness, finds vulnerabilities, and checks quality across architect-specified dimensions.",
@@ -105330,12 +105358,7 @@ A blank APPROVED without reasoning is NOT acceptable — it indicates you did no
105330
105358
  // src/agents/skill-improver.ts
105331
105359
  function createSkillImproverAgent(model, customPrompt, customAppendPrompt) {
105332
105360
  let prompt = DEFAULT_PROMPT;
105333
- if (customPrompt)
105334
- prompt = customPrompt;
105335
- else if (customAppendPrompt)
105336
- prompt = `${DEFAULT_PROMPT}
105337
-
105338
- ${customAppendPrompt}`;
105361
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
105339
105362
  const config3 = {
105340
105363
  model,
105341
105364
  temperature: 0.2,
@@ -105395,17 +105418,12 @@ least one non-empty predicate field (forbidden_actions / required_actions /
105395
105418
  verification_checks). Hardened entries return to the active store; entries
105396
105419
  that cannot be hardened are flagged retire_candidate for human review.
105397
105420
  `;
105421
+ var init_skill_improver2 = () => {};
105398
105422
 
105399
105423
  // src/agents/sme.ts
105400
105424
  function createSMEAgent(model, customPrompt, customAppendPrompt) {
105401
105425
  let prompt = SME_PROMPT;
105402
- if (customPrompt) {
105403
- prompt = customPrompt;
105404
- } else if (customAppendPrompt) {
105405
- prompt = `${SME_PROMPT}
105406
-
105407
- ${customAppendPrompt}`;
105408
- }
105426
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
105409
105427
  return {
105410
105428
  name: "sme",
105411
105429
  description: "Open-domain subject matter expert. Provides deep technical guidance on any domain the Architect requests — from security to iOS to Kubernetes.",
@@ -105562,12 +105580,7 @@ SME is read-only. Cache persistence is Architect's responsibility — save this
105562
105580
  // src/agents/spec-writer.ts
105563
105581
  function createSpecWriterAgent(model, customPrompt, customAppendPrompt) {
105564
105582
  let prompt = DEFAULT_PROMPT2;
105565
- if (customPrompt)
105566
- prompt = customPrompt;
105567
- else if (customAppendPrompt)
105568
- prompt = `${DEFAULT_PROMPT2}
105569
-
105570
- ${customAppendPrompt}`;
105583
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
105571
105584
  const config3 = {
105572
105585
  model,
105573
105586
  temperature: 0.2,
@@ -105611,6 +105624,7 @@ If a <swarm_knowledge_directives> block is present in your context, treat it
105611
105624
  as authoritative. For each applicable directive cite KNOWLEDGE_APPLIED: <id>
105612
105625
  in your reply, or KNOWLEDGE_IGNORED: <id> reason=... when it does not apply.
105613
105626
  `;
105627
+ var init_spec_writer = () => {};
105614
105628
 
105615
105629
  // src/agents/template.ts
105616
105630
  function emptyProjectContext() {
@@ -105641,13 +105655,7 @@ var UNRESOLVED = "unresolved (run /swarm preflight)";
105641
105655
  // src/agents/test-engineer.ts
105642
105656
  function createTestEngineerAgent(model, customPrompt, customAppendPrompt) {
105643
105657
  let prompt = TEST_ENGINEER_PROMPT;
105644
- if (customPrompt) {
105645
- prompt = customPrompt;
105646
- } else if (customAppendPrompt) {
105647
- prompt = `${TEST_ENGINEER_PROMPT}
105648
-
105649
- ${customAppendPrompt}`;
105650
- }
105658
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
105651
105659
  return {
105652
105660
  name: "test_engineer",
105653
105661
  description: "Testing and validation specialist. Generates test cases, runs them, and reports structured PASS/FAIL verdicts.",
@@ -105880,6 +105888,7 @@ COVERAGE REPORTING:
105880
105888
  - If COVERAGE_PCT < 70%, add a note: "COVERAGE_WARNING: Below 70% threshold — consider additional test cases for uncovered paths."
105881
105889
  - The architect uses this to decide whether to request an additional test pass (Rule 10 / Phase 5 step 5h).
105882
105890
  `;
105891
+ var init_test_engineer = () => {};
105883
105892
 
105884
105893
  // src/agents/index.ts
105885
105894
  import { mkdir as mkdir23, writeFile as writeFile19 } from "node:fs/promises";
@@ -106016,35 +106025,13 @@ If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the
106016
106025
  }
106017
106026
  agents.push(applyOverrides(architect, swarmAgents, swarmPrefix, quiet));
106018
106027
  }
106019
- if (!isAgentDisabled("explorer", swarmAgents, swarmPrefix)) {
106020
- const explorerPrompts = getPrompts("explorer");
106021
- const explorer = createExplorerAgent(getModel("explorer"), explorerPrompts.prompt, explorerPrompts.appendPrompt);
106022
- explorer.name = prefixName("explorer");
106023
- agents.push(applyOverrides(explorer, swarmAgents, swarmPrefix, quiet));
106024
- }
106025
- if (!isAgentDisabled("sme", swarmAgents, swarmPrefix)) {
106026
- const smePrompts = getPrompts("sme");
106027
- const sme = createSMEAgent(getModel("sme"), smePrompts.prompt, smePrompts.appendPrompt);
106028
- sme.name = prefixName("sme");
106029
- agents.push(applyOverrides(sme, swarmAgents, swarmPrefix, quiet));
106030
- }
106031
- if (!isAgentDisabled("researcher", swarmAgents, swarmPrefix)) {
106032
- const researcherPrompts = getPrompts("researcher");
106033
- const researcher = createResearcherAgent(getModel("researcher"), researcherPrompts.prompt, researcherPrompts.appendPrompt);
106034
- researcher.name = prefixName("researcher");
106035
- agents.push(applyOverrides(researcher, swarmAgents, swarmPrefix, quiet));
106036
- }
106037
- if (!isAgentDisabled("coder", swarmAgents, swarmPrefix)) {
106038
- const coderPrompts = getPrompts("coder");
106039
- const coder = createCoderAgent(getModel("coder"), coderPrompts.prompt, coderPrompts.appendPrompt);
106040
- coder.name = prefixName("coder");
106041
- agents.push(applyOverrides(coder, swarmAgents, swarmPrefix, quiet));
106042
- }
106043
- if (!isAgentDisabled("reviewer", swarmAgents, swarmPrefix)) {
106044
- const reviewerPrompts = getPrompts("reviewer");
106045
- const reviewer = createReviewerAgent(getModel("reviewer"), reviewerPrompts.prompt, reviewerPrompts.appendPrompt);
106046
- reviewer.name = prefixName("reviewer");
106047
- agents.push(applyOverrides(reviewer, swarmAgents, swarmPrefix, quiet));
106028
+ for (const { name, factory } of TYPE_A_AGENTS) {
106029
+ if (!isAgentDisabled(name, swarmAgents, swarmPrefix)) {
106030
+ const prompts = getPrompts(name);
106031
+ const agent = factory(getModel(name), prompts.prompt, prompts.appendPrompt);
106032
+ agent.name = prefixName(name);
106033
+ agents.push(applyOverrides(agent, swarmAgents, swarmPrefix, quiet));
106034
+ }
106048
106035
  }
106049
106036
  if (!isAgentDisabled("critic", swarmAgents, swarmPrefix)) {
106050
106037
  const criticPrompts = getPrompts("critic");
@@ -106101,24 +106088,6 @@ If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the
106101
106088
  curatorConsolidation.name = prefixName("curator_consolidation");
106102
106089
  agents.push(applyOverrides(curatorConsolidation, swarmAgents, swarmPrefix, quiet));
106103
106090
  }
106104
- if (!isAgentDisabled("skill_improver", swarmAgents, swarmPrefix)) {
106105
- const sip = getPrompts("skill_improver");
106106
- const skillImprover = createSkillImproverAgent(swarmAgents?.skill_improver?.model ?? getModel("skill_improver"), sip.prompt, sip.appendPrompt);
106107
- skillImprover.name = prefixName("skill_improver");
106108
- agents.push(applyOverrides(skillImprover, swarmAgents, swarmPrefix, quiet));
106109
- }
106110
- if (!isAgentDisabled("spec_writer", swarmAgents, swarmPrefix)) {
106111
- const sw = getPrompts("spec_writer");
106112
- const specWriter = createSpecWriterAgent(swarmAgents?.spec_writer?.model ?? getModel("spec_writer"), sw.prompt, sw.appendPrompt);
106113
- specWriter.name = prefixName("spec_writer");
106114
- agents.push(applyOverrides(specWriter, swarmAgents, swarmPrefix, quiet));
106115
- }
106116
- if (!isAgentDisabled("test_engineer", swarmAgents, swarmPrefix)) {
106117
- const testPrompts = getPrompts("test_engineer");
106118
- const testEngineer = createTestEngineerAgent(getModel("test_engineer"), testPrompts.prompt, testPrompts.appendPrompt);
106119
- testEngineer.name = prefixName("test_engineer");
106120
- agents.push(applyOverrides(testEngineer, swarmAgents, swarmPrefix, quiet));
106121
- }
106122
106091
  if (pluginConfig?.council?.general?.enabled === true) {
106123
106092
  let councilAgentsCreated = 0;
106124
106093
  if (!isAgentDisabled("reviewer", swarmAgents, swarmPrefix)) {
@@ -106153,12 +106122,6 @@ If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the
106153
106122
  addDeferredWarning("[opencode-swarm] council.general.moderatorModel is deprecated and ignored. The architect now synthesizes the final answer directly using inline output rules. Remove this field (and council.general.moderator if set) from opencode-swarm.json to silence this warning.");
106154
106123
  }
106155
106124
  }
106156
- if (!isAgentDisabled("docs", swarmAgents, swarmPrefix)) {
106157
- const docsPrompts = getPrompts("docs");
106158
- const docs = createDocsAgent(getModel("docs"), docsPrompts.prompt, docsPrompts.appendPrompt);
106159
- docs.name = prefixName("docs");
106160
- agents.push(applyOverrides(docs, swarmAgents, swarmPrefix, quiet));
106161
- }
106162
106125
  if (pluginConfig?.design_docs?.enabled === true && !isAgentDisabled("docs_design", swarmAgents, swarmPrefix)) {
106163
106126
  const docsDesignPrompts = getPrompts("docs_design");
106164
106127
  const docsDesign = createDocsAgent(getModel("docs_design"), docsDesignPrompts.prompt, docsDesignPrompts.appendPrompt, "design_docs");
@@ -106393,30 +106356,38 @@ function getAgentConfigs(config3, directory, sessionId, projectContext) {
106393
106356
  }
106394
106357
  return result;
106395
106358
  }
106396
- var warnedAgents, _swarmAgentsMap, KNOWN_VARIANT_VALUES;
106359
+ var warnedAgents, _swarmAgentsMap, KNOWN_VARIANT_VALUES, TYPE_A_AGENTS;
106397
106360
  var init_agents2 = __esm(() => {
106398
106361
  init_config();
106399
106362
  init_constants();
106400
106363
  init_schema();
106401
106364
  init_warning_buffer();
106402
106365
  init_architect();
106366
+ init_coder();
106403
106367
  init_council_prompts();
106404
106368
  init_critic();
106405
106369
  init_curator_agent();
106370
+ init_designer();
106406
106371
  init_docs();
106407
106372
  init_explorer();
106408
106373
  init_researcher();
106409
106374
  init_reviewer();
106375
+ init_skill_improver2();
106410
106376
  init_sme();
106377
+ init_spec_writer();
106378
+ init_test_engineer();
106411
106379
  init_architect();
106380
+ init_coder();
106412
106381
  init_council_prompts();
106413
106382
  init_critic();
106414
106383
  init_curator_agent();
106384
+ init_designer();
106415
106385
  init_docs();
106416
106386
  init_explorer();
106417
106387
  init_researcher();
106418
106388
  init_reviewer();
106419
106389
  init_sme();
106390
+ init_test_engineer();
106420
106391
  warnedAgents = new Set;
106421
106392
  _swarmAgentsMap = new Map;
106422
106393
  KNOWN_VARIANT_VALUES = new Set([
@@ -106427,6 +106398,17 @@ var init_agents2 = __esm(() => {
106427
106398
  "xhigh",
106428
106399
  "thinking"
106429
106400
  ]);
106401
+ TYPE_A_AGENTS = [
106402
+ { name: "explorer", factory: createExplorerAgent },
106403
+ { name: "sme", factory: createSMEAgent },
106404
+ { name: "researcher", factory: createResearcherAgent },
106405
+ { name: "coder", factory: createCoderAgent },
106406
+ { name: "reviewer", factory: createReviewerAgent },
106407
+ { name: "test_engineer", factory: createTestEngineerAgent },
106408
+ { name: "docs", factory: createDocsAgent },
106409
+ { name: "skill_improver", factory: createSkillImproverAgent },
106410
+ { name: "spec_writer", factory: createSpecWriterAgent }
106411
+ ];
106430
106412
  });
106431
106413
 
106432
106414
  // src/background/evidence-summary-integration.ts
@@ -123877,11 +123859,6 @@ function isSubagentDelegation(toolName, args2) {
123877
123859
  return true;
123878
123860
  return false;
123879
123861
  }
123880
- function normalizePath4(p) {
123881
- if (!p)
123882
- return "";
123883
- return p.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
123884
- }
123885
123862
  function isWithinDirectory(target, root) {
123886
123863
  if (!target || !root)
123887
123864
  return false;
@@ -123908,9 +123885,9 @@ var SEGMENT_PROTECTED = new Set([
123908
123885
  function isProtectedPath2(filePath, config3) {
123909
123886
  if (!filePath)
123910
123887
  return false;
123911
- const normalized = normalizePath4(filePath);
123888
+ const normalized = normalizePath(filePath);
123912
123889
  for (const prefix of ALWAYS_PROTECTED_PREFIXES) {
123913
- const np = normalizePath4(prefix);
123890
+ const np = normalizePath(prefix);
123914
123891
  if (normalized === np || normalized.startsWith(`${np}/`))
123915
123892
  return true;
123916
123893
  }
@@ -123927,7 +123904,7 @@ function isProtectedPath2(filePath, config3) {
123927
123904
  ...config3?.permission_policy?.protected_paths ?? []
123928
123905
  ];
123929
123906
  for (const candidate of protectedList) {
123930
- const c = normalizePath4(candidate);
123907
+ const c = normalizePath(candidate);
123931
123908
  if (!c)
123932
123909
  continue;
123933
123910
  if (normalized === c)
@@ -123976,18 +123953,18 @@ function classifyPathRisk(filePath, context) {
123976
123953
  }
123977
123954
  })();
123978
123955
  const withinProjectRoot = isWithinDirectory(absolute, context.directory) && isWithinDirectory(resolvedAbsolute, context.directory);
123979
- const relative26 = path151.relative(context.directory, absolute).replace(/\\/g, "/");
123956
+ const relative26 = normalizePath(path151.relative(context.directory, absolute));
123980
123957
  let withinDeclaredScope = null;
123981
123958
  if (Array.isArray(context.declaredScope)) {
123982
123959
  withinDeclaredScope = context.declaredScope.length === 0 ? false : context.declaredScope.some((scope) => {
123983
- const s = normalizePath4(scope);
123960
+ const s = normalizePath(scope);
123984
123961
  if (!s)
123985
123962
  return false;
123986
123963
  return relative26 === s || relative26.startsWith(`${s}/`);
123987
123964
  });
123988
123965
  }
123989
123966
  const highRiskBuild = HIGH_RISK_BUILD_PATHS.some((entry) => {
123990
- const e = normalizePath4(entry);
123967
+ const e = normalizePath(entry);
123991
123968
  if (e.endsWith("/")) {
123992
123969
  return relative26.startsWith(e);
123993
123970
  }
@@ -151637,7 +151614,7 @@ var SKIP_DIRS = [
151637
151614
  var SKIP_EXTENSIONS = [".test.", ".spec."];
151638
151615
  var MAX_SPEC_SIZE = 10 * 1024 * 1024;
151639
151616
  var ALLOWED_EXTENSIONS = [".json", ".yaml", ".yml"];
151640
- function normalizePath5(p) {
151617
+ function normalizeApiPath(p) {
151641
151618
  return p.replace(/\/$/, "").replace(/\{[^}]+\}/g, ":param").replace(/:[a-zA-Z_][a-zA-Z0-9_]*/g, ":param");
151642
151619
  }
151643
151620
  function discoverSpecFile(cwd, specFileArg) {
@@ -151814,7 +151791,7 @@ function extractRoutesFromFile(filePath) {
151814
151791
  function findDrift(specPaths, codeRoutes) {
151815
151792
  const specPathMap = new Map;
151816
151793
  for (const sp of specPaths) {
151817
- const normalized = normalizePath5(sp.path);
151794
+ const normalized = normalizeApiPath(sp.path);
151818
151795
  if (!specPathMap.has(normalized)) {
151819
151796
  specPathMap.set(normalized, []);
151820
151797
  }
@@ -151822,7 +151799,7 @@ function findDrift(specPaths, codeRoutes) {
151822
151799
  }
151823
151800
  const codeRouteMap = new Map;
151824
151801
  for (const cr of codeRoutes) {
151825
- const normalized = normalizePath5(cr.path);
151802
+ const normalized = normalizeApiPath(cr.path);
151826
151803
  if (!codeRouteMap.has(normalized)) {
151827
151804
  codeRouteMap.set(normalized, []);
151828
151805
  }
@@ -151845,7 +151822,7 @@ function findDrift(specPaths, codeRoutes) {
151845
151822
  for (const [normalized, methods] of specPathMap) {
151846
151823
  if (!codeRouteMap.has(normalized)) {
151847
151824
  phantom.push({
151848
- path: specPaths.find((sp) => normalizePath5(sp.path) === normalized).path,
151825
+ path: specPaths.find((sp) => normalizeApiPath(sp.path) === normalized).path,
151849
151826
  methods
151850
151827
  });
151851
151828
  }
@@ -18,6 +18,8 @@
18
18
  * All paths are normalized to POSIX-style (forward slashes, no trailing slash)
19
19
  * before conflict detection. This ensures consistent behavior across platforms.
20
20
  */
21
+ import { normalizePath } from '../../utils/path';
22
+ export { normalizePath };
21
23
  /**
22
24
  * A scope file persisted by the `declare_scope` tool.
23
25
  * Stored at `.swarm/scopes/scope-{taskId}.json`.
@@ -32,19 +34,6 @@ export interface ScopeFile {
32
34
  * These are treated as global because other tasks may import from them.
33
35
  */
34
36
  export declare const BARREL_FILE_PATTERNS: readonly RegExp[];
35
- /**
36
- * Normalize a file path to POSIX-style for consistent cross-platform comparison.
37
- *
38
- * - Converts backslashes to forward slashes
39
- * - Removes trailing slashes
40
- * - Collapses multiple consecutive slashes
41
- * - Resolves `.` path segments (current directory references)
42
- * - Does NOT resolve `..` segments or symlinks
43
- *
44
- * @param filePath - The path to normalize
45
- * @returns POSIX-normalized path
46
- */
47
- export declare function normalizePath(filePath: string): string;
48
37
  /**
49
38
  * Check if a path contains directory traversal components.
50
39
  * Rejects paths with `..` segments that could escape the project root.
@@ -0,0 +1 @@
1
+ export declare function normalizePath(p: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.99.0",
3
+ "version": "7.99.1",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",