opencode-swarm 6.23.1 → 6.24.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/dist/index.js CHANGED
@@ -15749,6 +15749,39 @@ async function savePlan(directory, plan) {
15749
15749
  throw new Error(`Invalid directory: directory must be a non-empty string`);
15750
15750
  }
15751
15751
  const validated = PlanSchema.parse(plan);
15752
+ try {
15753
+ const currentPlan = await loadPlanJsonOnly(directory);
15754
+ if (currentPlan) {
15755
+ const completedTaskIds = new Set;
15756
+ for (const phase of currentPlan.phases) {
15757
+ for (const task of phase.tasks) {
15758
+ if (task.status === "completed")
15759
+ completedTaskIds.add(task.id);
15760
+ }
15761
+ }
15762
+ if (completedTaskIds.size > 0) {
15763
+ for (const phase of validated.phases) {
15764
+ for (const task of phase.tasks) {
15765
+ if (completedTaskIds.has(task.id) && task.status !== "completed") {
15766
+ task.status = "completed";
15767
+ }
15768
+ }
15769
+ }
15770
+ }
15771
+ }
15772
+ } catch {}
15773
+ for (const phase of validated.phases) {
15774
+ const tasks = phase.tasks;
15775
+ if (tasks.length > 0 && tasks.every((t) => t.status === "completed")) {
15776
+ phase.status = "complete";
15777
+ } else if (tasks.some((t) => t.status === "in_progress")) {
15778
+ phase.status = "in_progress";
15779
+ } else if (tasks.some((t) => t.status === "blocked")) {
15780
+ phase.status = "blocked";
15781
+ } else {
15782
+ phase.status = "pending";
15783
+ }
15784
+ }
15752
15785
  const swarmDir = path4.resolve(directory, ".swarm");
15753
15786
  const planPath = path4.join(swarmDir, "plan.json");
15754
15787
  const tempPath = path4.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
@@ -35824,6 +35857,133 @@ var init_preflight_service = __esm(() => {
35824
35857
  };
35825
35858
  });
35826
35859
 
35860
+ // src/gate-evidence.ts
35861
+ var exports_gate_evidence = {};
35862
+ __export(exports_gate_evidence, {
35863
+ recordGateEvidence: () => recordGateEvidence,
35864
+ recordAgentDispatch: () => recordAgentDispatch,
35865
+ readTaskEvidence: () => readTaskEvidence,
35866
+ hasPassedAllGates: () => hasPassedAllGates,
35867
+ expandRequiredGates: () => expandRequiredGates,
35868
+ deriveRequiredGates: () => deriveRequiredGates,
35869
+ DEFAULT_REQUIRED_GATES: () => DEFAULT_REQUIRED_GATES
35870
+ });
35871
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, renameSync as renameSync7, unlinkSync as unlinkSync4 } from "fs";
35872
+ import * as path27 from "path";
35873
+ function assertValidTaskId(taskId) {
35874
+ if (!taskId || taskId.includes("..") || taskId.includes("/") || taskId.includes("\\") || taskId.includes("\x00") || !TASK_ID_PATTERN.test(taskId)) {
35875
+ throw new Error(`Invalid taskId: "${taskId}". Must match N.M or N.M.P (e.g. "1.1", "1.2.3").`);
35876
+ }
35877
+ }
35878
+ function deriveRequiredGates(agentType) {
35879
+ switch (agentType) {
35880
+ case "coder":
35881
+ return ["reviewer", "test_engineer"];
35882
+ case "docs":
35883
+ return ["docs"];
35884
+ case "designer":
35885
+ return ["designer", "reviewer", "test_engineer"];
35886
+ case "explorer":
35887
+ return ["explorer"];
35888
+ case "sme":
35889
+ return ["sme"];
35890
+ case "reviewer":
35891
+ return ["reviewer"];
35892
+ case "test_engineer":
35893
+ return ["test_engineer"];
35894
+ case "critic":
35895
+ return ["critic"];
35896
+ default:
35897
+ return ["reviewer", "test_engineer"];
35898
+ }
35899
+ }
35900
+ function expandRequiredGates(existingGates, newAgentType) {
35901
+ const newGates = deriveRequiredGates(newAgentType);
35902
+ const combined = [...new Set([...existingGates, ...newGates])];
35903
+ return combined.sort();
35904
+ }
35905
+ function getEvidenceDir(directory) {
35906
+ return path27.join(directory, ".swarm", "evidence");
35907
+ }
35908
+ function getEvidencePath(directory, taskId) {
35909
+ return path27.join(getEvidenceDir(directory), `${taskId}.json`);
35910
+ }
35911
+ function readExisting(evidencePath) {
35912
+ try {
35913
+ const raw = readFileSync13(evidencePath, "utf-8");
35914
+ return JSON.parse(raw);
35915
+ } catch {
35916
+ return null;
35917
+ }
35918
+ }
35919
+ async function atomicWrite(targetPath, content) {
35920
+ const tempPath = `${targetPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
35921
+ try {
35922
+ await Bun.write(tempPath, content);
35923
+ renameSync7(tempPath, targetPath);
35924
+ } finally {
35925
+ try {
35926
+ unlinkSync4(tempPath);
35927
+ } catch {}
35928
+ }
35929
+ }
35930
+ async function recordGateEvidence(directory, taskId, gate, sessionId) {
35931
+ assertValidTaskId(taskId);
35932
+ const evidenceDir = getEvidenceDir(directory);
35933
+ const evidencePath = getEvidencePath(directory, taskId);
35934
+ mkdirSync8(evidenceDir, { recursive: true });
35935
+ const existing = readExisting(evidencePath);
35936
+ const requiredGates = existing ? expandRequiredGates(existing.required_gates, gate) : deriveRequiredGates(gate);
35937
+ const updated = {
35938
+ taskId,
35939
+ required_gates: requiredGates,
35940
+ gates: {
35941
+ ...existing?.gates ?? {},
35942
+ [gate]: {
35943
+ sessionId,
35944
+ timestamp: new Date().toISOString(),
35945
+ agent: gate
35946
+ }
35947
+ }
35948
+ };
35949
+ await atomicWrite(evidencePath, JSON.stringify(updated, null, 2));
35950
+ }
35951
+ async function recordAgentDispatch(directory, taskId, agentType) {
35952
+ assertValidTaskId(taskId);
35953
+ const evidenceDir = getEvidenceDir(directory);
35954
+ const evidencePath = getEvidencePath(directory, taskId);
35955
+ mkdirSync8(evidenceDir, { recursive: true });
35956
+ const existing = readExisting(evidencePath);
35957
+ const requiredGates = existing ? expandRequiredGates(existing.required_gates, agentType) : deriveRequiredGates(agentType);
35958
+ const updated = {
35959
+ taskId,
35960
+ required_gates: requiredGates,
35961
+ gates: existing?.gates ?? {}
35962
+ };
35963
+ await atomicWrite(evidencePath, JSON.stringify(updated, null, 2));
35964
+ }
35965
+ async function readTaskEvidence(directory, taskId) {
35966
+ try {
35967
+ assertValidTaskId(taskId);
35968
+ return readExisting(getEvidencePath(directory, taskId));
35969
+ } catch {
35970
+ return null;
35971
+ }
35972
+ }
35973
+ async function hasPassedAllGates(directory, taskId) {
35974
+ const evidence = await readTaskEvidence(directory, taskId);
35975
+ if (!evidence)
35976
+ return false;
35977
+ if (!Array.isArray(evidence.required_gates) || evidence.required_gates.length === 0)
35978
+ return false;
35979
+ return evidence.required_gates.every((gate) => evidence.gates[gate] != null);
35980
+ }
35981
+ var DEFAULT_REQUIRED_GATES, TASK_ID_PATTERN;
35982
+ var init_gate_evidence = __esm(() => {
35983
+ DEFAULT_REQUIRED_GATES = ["reviewer", "test_engineer"];
35984
+ TASK_ID_PATTERN = /^\d+\.\d+(\.\d+)*$/;
35985
+ });
35986
+
35827
35987
  // src/services/preflight-integration.ts
35828
35988
  var exports_preflight_integration = {};
35829
35989
  __export(exports_preflight_integration, {
@@ -37353,11 +37513,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37353
37513
  throw toThrow;
37354
37514
  }, "quit_");
37355
37515
  var scriptDirectory = "";
37356
- function locateFile(path39) {
37516
+ function locateFile(path40) {
37357
37517
  if (Module["locateFile"]) {
37358
- return Module["locateFile"](path39, scriptDirectory);
37518
+ return Module["locateFile"](path40, scriptDirectory);
37359
37519
  }
37360
- return scriptDirectory + path39;
37520
+ return scriptDirectory + path40;
37361
37521
  }
37362
37522
  __name(locateFile, "locateFile");
37363
37523
  var readAsync, readBinary;
@@ -39105,7 +39265,7 @@ var init_runtime = __esm(() => {
39105
39265
  });
39106
39266
 
39107
39267
  // src/index.ts
39108
- import * as path48 from "path";
39268
+ import * as path49 from "path";
39109
39269
 
39110
39270
  // src/agents/index.ts
39111
39271
  init_config();
@@ -39945,15 +40105,6 @@ Swarm: {{SWARM_ID}}
39945
40105
  ## Patterns
39946
40106
  - <pattern name>: <how and when to use it in this codebase>
39947
40107
 
39948
- ROLE-RELEVANCE TAGGING
39949
- When writing output consumed by other agents, prefix with:
39950
- [FOR: agent1, agent2] \u2014 relevant to specific agents
39951
- [FOR: ALL] \u2014 relevant to all agents
39952
- Examples:
39953
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
39954
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
39955
- [FOR: ALL] "Breaking change: StateManager renamed"
39956
- This tag is informational in v6.19; v6.20 will use for context filtering.
39957
40108
  `;
39958
40109
  function createArchitectAgent(model, customPrompt, customAppendPrompt, adversarialTesting) {
39959
40110
  let prompt = ARCHITECT_PROMPT;
@@ -40009,15 +40160,44 @@ RULES:
40009
40160
  - No research, no web searches, no documentation lookups
40010
40161
  - Use training knowledge for APIs
40011
40162
 
40012
- OUTPUT FORMAT:
40163
+ ## DEFENSIVE CODING RULES
40164
+ - NEVER use \`any\` type in TypeScript \u2014 always use specific types
40165
+ - NEVER leave empty catch blocks \u2014 at minimum log the error
40166
+ - NEVER use string concatenation for paths \u2014 use \`path.join()\` or \`path.resolve()\`
40167
+ - NEVER use platform-specific path separators \u2014 use \`path.join()\` for all path construction
40168
+ - NEVER import from relative paths traversing more than 2 levels (\`../../..\`) \u2014 use path aliases
40169
+ - NEVER use synchronous fs methods in async contexts unless explicitly required by the task
40170
+ - PREFER early returns over deeply nested conditionals
40171
+ - PREFER \`const\` over \`let\`; never use \`var\`
40172
+ - When modifying existing code, MATCH the surrounding style (indentation, quote style, semicolons)
40173
+
40174
+ ## ERROR HANDLING
40175
+ When your implementation encounters an error or unexpected state:
40176
+ 1. DO NOT silently swallow errors
40177
+ 2. DO NOT invent workarounds not specified in the task
40178
+ 3. DO NOT modify files outside the CONSTRAINT boundary to "fix" the issue
40179
+ 4. Report the blocker using this format:
40180
+ BLOCKED: [what went wrong]
40181
+ NEED: [what additional context or change would fix it]
40182
+ The architect will re-scope or provide additional context. You are not authorized to make scope decisions.
40183
+
40184
+ OUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected):
40185
+ For a completed task, begin directly with DONE.
40186
+ If the task is blocked, begin directly with BLOCKED.
40187
+ Do NOT prepend "Here's what I changed..." or any conversational preamble.
40188
+
40013
40189
  DONE: [one-line summary]
40014
40190
  CHANGED: [file]: [what changed]
40191
+ BLOCKED: [what went wrong]
40192
+ NEED: [what additional context or change would fix it]
40015
40193
 
40016
40194
  AUTHOR BLINDNESS WARNING:
40017
40195
  Your output is NOT reviewed, tested, or approved until the Architect runs the full QA gate.
40018
40196
  Do NOT add commentary like "this looks good," "should be fine," or "ready for production."
40019
40197
  You wrote the code. You cannot objectively evaluate it. That is what the gates are for.
40020
- Output only: DONE [one-line summary] / CHANGED [file] [what changed]
40198
+ Output only one of:
40199
+ - DONE [one-line summary] / CHANGED [file] [what changed]
40200
+ - BLOCKED [what went wrong] / NEED [what additional context or change would fix it]
40021
40201
 
40022
40202
  SELF-AUDIT (run before marking any task complete):
40023
40203
  Before you report task completion, verify:
@@ -40040,15 +40220,6 @@ META.SUMMARY CONVENTION \u2014 When reporting task completion, include:
40040
40220
 
40041
40221
  Write for the next agent reading the event log, not for a human.
40042
40222
 
40043
- ROLE-RELEVANCE TAGGING
40044
- When writing output consumed by other agents, prefix with:
40045
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40046
- [FOR: ALL] \u2014 relevant to all agents
40047
- Examples:
40048
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40049
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40050
- [FOR: ALL] "Breaking change: StateManager renamed"
40051
- This tag is informational in v6.19; v6.20 will use for context filtering.
40052
40223
  `;
40053
40224
  function createCoderAgent(model, customPrompt, customAppendPrompt) {
40054
40225
  let prompt = CODER_PROMPT;
@@ -40249,15 +40420,6 @@ SOUNDING_BOARD RULES:
40249
40420
  - Do not use Task tool \u2014 evaluate directly
40250
40421
  - Read-only: do not create, modify, or delete any file
40251
40422
 
40252
- ROLE-RELEVANCE TAGGING
40253
- When writing output consumed by other agents, prefix with:
40254
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40255
- [FOR: ALL] \u2014 relevant to all agents
40256
- Examples:
40257
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40258
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40259
- [FOR: ALL] "Breaking change: StateManager renamed"
40260
- This tag is informational in v6.19; v6.20 will use for context filtering.
40261
40423
  `;
40262
40424
  function createCriticAgent(model, customPrompt, customAppendPrompt) {
40263
40425
  let prompt = CRITIC_PROMPT;
@@ -40417,15 +40579,6 @@ RULES:
40417
40579
  - Do NOT implement business logic \u2014 leave that for the coder
40418
40580
  - Keep output under 3000 characters per component
40419
40581
 
40420
- ROLE-RELEVANCE TAGGING
40421
- When writing output consumed by other agents, prefix with:
40422
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40423
- [FOR: ALL] \u2014 relevant to all agents
40424
- Examples:
40425
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40426
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40427
- [FOR: ALL] "Breaking change: StateManager renamed"
40428
- This tag is informational in v6.19; v6.20 will use for context filtering.
40429
40582
  `;
40430
40583
  function createDesignerAgent(model, customPrompt, customAppendPrompt) {
40431
40584
  let prompt = DESIGNER_PROMPT;
@@ -40487,6 +40640,28 @@ WORKFLOW:
40487
40640
  b. Update JSDoc/docstring comments to match new signatures and behavior
40488
40641
  c. Add missing documentation for new exports
40489
40642
 
40643
+ ## DOCUMENTATION SCOPE
40644
+
40645
+ ### ALWAYS update (when present):
40646
+ - README.md: If public API changed, update usage examples
40647
+ - CHANGELOG.md: Add entry under \`## [Unreleased]\` using Keep a Changelog format:
40648
+ ## [Unreleased]
40649
+ ### Added
40650
+ - New feature description
40651
+ ### Changed
40652
+ - Existing behavior that was modified
40653
+ ### Fixed
40654
+ - Bug that was resolved
40655
+ ### Removed
40656
+ - Feature or code that was removed
40657
+ - API docs: If function signatures changed, update JSDoc/TSDoc in source files
40658
+ - Type definitions: If exported types changed, ensure documentation is current
40659
+
40660
+ ### NEVER create:
40661
+ - New documentation files not requested by the architect
40662
+ - Inline comments explaining obvious code (code should be self-documenting)
40663
+ - TODO comments in code (those go through the task system, not code comments)
40664
+
40490
40665
  RULES:
40491
40666
  - Be accurate: documentation MUST match the actual code behavior
40492
40667
  - Be concise: update only what changed, do not rewrite entire files
@@ -40495,21 +40670,13 @@ RULES:
40495
40670
  - No fabrication: if you cannot determine behavior from the code, say so explicitly
40496
40671
  - Update version references if package.json version changed
40497
40672
 
40498
- OUTPUT FORMAT:
40673
+ OUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected):
40674
+ Begin directly with UPDATED. Do NOT prepend "Here's what I updated..." or any conversational preamble.
40675
+
40499
40676
  UPDATED: [list of files modified]
40500
40677
  ADDED: [list of new sections/files created]
40501
40678
  REMOVED: [list of deprecated sections removed]
40502
40679
  SUMMARY: [one-line description of doc changes]
40503
-
40504
- ROLE-RELEVANCE TAGGING
40505
- When writing output consumed by other agents, prefix with:
40506
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40507
- [FOR: ALL] \u2014 relevant to all agents
40508
- Examples:
40509
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40510
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40511
- [FOR: ALL] "Breaking change: StateManager renamed"
40512
- This tag is informational in v6.19; v6.20 will use for context filtering.
40513
40680
  `;
40514
40681
  function createDocsAgent(model, customPrompt, customAppendPrompt) {
40515
40682
  let prompt = DOCS_PROMPT;
@@ -40554,7 +40721,36 @@ RULES:
40554
40721
  - No code modifications
40555
40722
  - Output under 2000 chars
40556
40723
 
40557
- OUTPUT FORMAT:
40724
+ ## ANALYSIS PROTOCOL
40725
+ When exploring a codebase area, systematically report all four dimensions:
40726
+
40727
+ ### STRUCTURE
40728
+ - Entry points and their call chains (max 3 levels deep)
40729
+ - Public API surface: exported functions/classes/types with signatures
40730
+ - Internal dependencies: what this module imports and from where
40731
+ - External dependencies: third-party packages used
40732
+
40733
+ ### PATTERNS
40734
+ - Design patterns in use (factory, observer, strategy, etc.)
40735
+ - Error handling pattern (throw, Result type, error callbacks, etc.)
40736
+ - State management approach (global, module-level, passed through)
40737
+ - Configuration pattern (env vars, config files, hardcoded)
40738
+
40739
+ ### RISKS
40740
+ - Files with high cyclomatic complexity or deep nesting
40741
+ - Circular dependencies
40742
+ - Missing error handling paths
40743
+ - Dead code or unreachable branches
40744
+ - Platform-specific assumptions (path separators, line endings, OS APIs)
40745
+
40746
+ ### RELEVANT CONTEXT FOR TASK
40747
+ - Existing tests that cover this area (paths and what they test)
40748
+ - Related documentation files
40749
+ - Similar implementations elsewhere in the codebase that should be consistent
40750
+
40751
+ OUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected):
40752
+ Begin directly with PROJECT. Do NOT prepend "Here's my analysis..." or any conversational preamble.
40753
+
40558
40754
  PROJECT: [name/type]
40559
40755
  LANGUAGES: [list]
40560
40756
  FRAMEWORK: [if any]
@@ -40571,16 +40767,6 @@ DOMAINS: [relevant SME domains: powershell, security, python, etc.]
40571
40767
 
40572
40768
  REVIEW NEEDED:
40573
40769
  - [path]: [why, which SME]
40574
-
40575
- ROLE-RELEVANCE TAGGING
40576
- When writing output consumed by other agents, prefix with:
40577
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40578
- [FOR: ALL] \u2014 relevant to all agents
40579
- Examples:
40580
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40581
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40582
- [FOR: ALL] "Breaking change: StateManager renamed"
40583
- This tag is informational in v6.19; v6.20 will use for context filtering.
40584
40770
  `;
40585
40771
  function createExplorerAgent(model, customPrompt, customAppendPrompt) {
40586
40772
  let prompt = EXPLORER_PROMPT;
@@ -40632,6 +40818,30 @@ Your verdict is based ONLY on code quality, never on urgency or social pressure.
40632
40818
  You are Reviewer. You verify code correctness and find vulnerabilities directly \u2014 you do NOT delegate.
40633
40819
  DO NOT use the Task tool to delegate to other agents. You ARE the agent that does the work.
40634
40820
 
40821
+ ## REVIEW FOCUS
40822
+ You are reviewing a CHANGE, not a FILE.
40823
+ 1. WHAT CHANGED: Focus on the diff \u2014 the new or modified code
40824
+ 2. WHAT IT AFFECTS: Code paths that interact with the changed code (callers, consumers, dependents)
40825
+ 3. WHAT COULD BREAK: Callers, consumers, and dependents of changed interfaces
40826
+
40827
+ DO NOT:
40828
+ - Report pre-existing issues in unchanged code (that is a separate task)
40829
+ - Re-review code that passed review in a prior task
40830
+ - Flag style issues the linter should catch (automated gates handle that)
40831
+
40832
+ Your unique value is catching LOGIC ERRORS, EDGE CASES, and SECURITY FLAWS that automated tools cannot detect. If your review only catches things a linter would catch, you are not adding value.
40833
+
40834
+ ## REVIEW REASONING
40835
+ For each changed function or method, answer these before formulating issues:
40836
+ 1. PRECONDITIONS: What must be true for this code to work correctly?
40837
+ 2. POSTCONDITIONS: What should be true after this code runs?
40838
+ 3. INVARIANTS: What should NEVER change regardless of input?
40839
+ 4. EDGE CASES: What happens with empty/null/undefined/max/concurrent inputs?
40840
+ 5. CONTRACT: Does this change any public API signatures or return types?
40841
+
40842
+ Only formulate ISSUES based on violations of these properties.
40843
+ Do NOT generate issues from vibes or pattern-matching alone.
40844
+
40635
40845
  ## REVIEW STRUCTURE \u2014 THREE TIERS
40636
40846
 
40637
40847
  STEP 0: INTENT RECONSTRUCTION (mandatory, before Tier 1)
@@ -40663,10 +40873,14 @@ VERBOSITY CONTROL: Token budget \u2264800 tokens. TRIVIAL APPROVED = 2-3 lines.
40663
40873
 
40664
40874
  ## INPUT FORMAT
40665
40875
  TASK: Review [description]
40666
- FILE: [path]
40876
+ FILE: [primary changed file or diff entry point]
40877
+ DIFF: [changed files/functions, or "infer from FILE" if omitted]
40878
+ AFFECTS: [callers/consumers/dependents to inspect, or "infer from diff"]
40667
40879
  CHECK: [list of dimensions to evaluate]
40668
40880
 
40669
- ## OUTPUT FORMAT
40881
+ ## OUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected)
40882
+ Begin directly with VERDICT. Do NOT prepend "Here's my review..." or any conversational preamble.
40883
+
40670
40884
  VERDICT: APPROVED | REJECTED
40671
40885
  RISK: LOW | MEDIUM | HIGH | CRITICAL
40672
40886
  ISSUES: list with line numbers, grouped by CHECK dimension
@@ -40684,15 +40898,6 @@ FIXES: required changes if rejected
40684
40898
  - HIGH: must fix
40685
40899
  - CRITICAL: blocks approval
40686
40900
 
40687
- ROLE-RELEVANCE TAGGING
40688
- When writing output consumed by other agents, prefix with:
40689
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40690
- [FOR: ALL] \u2014 relevant to all agents
40691
- Examples:
40692
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40693
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40694
- [FOR: ALL] "Breaking change: StateManager renamed"
40695
- This tag is informational in v6.19; v6.20 will use for context filtering.
40696
40901
  `;
40697
40902
  function createReviewerAgent(model, customPrompt, customAppendPrompt) {
40698
40903
  let prompt = REVIEWER_PROMPT;
@@ -40724,6 +40929,23 @@ var SME_PROMPT = `## IDENTITY
40724
40929
  You are SME (Subject Matter Expert). You provide deep domain-specific technical guidance directly \u2014 you do NOT delegate.
40725
40930
  DO NOT use the Task tool to delegate to other agents. You ARE the agent that does the work.
40726
40931
 
40932
+ ## RESEARCH PROTOCOL
40933
+ When consulting on a domain question, follow these steps in order:
40934
+ 1. FRAME: Restate the question in one sentence to confirm understanding
40935
+ 2. CONTEXT: What you already know from training about this domain
40936
+ 3. CONSTRAINTS: Platform, language, or framework constraints that apply
40937
+ 4. RECOMMENDATION: Your specific, actionable recommendation
40938
+ 5. ALTERNATIVES: Other viable approaches (max 2) with trade-offs
40939
+ 6. RISKS: What could go wrong with the recommended approach
40940
+ 7. CONFIDENCE: HIGH / MEDIUM / LOW (see calibration below)
40941
+
40942
+ ## CONFIDENCE CALIBRATION
40943
+ - HIGH: You can cite specific documentation, RFCs, or well-established patterns
40944
+ - MEDIUM: You are reasoning from general principles and similar patterns
40945
+ - LOW: You are speculating, or the domain is rapidly evolving \u2014 use this honestly
40946
+
40947
+ DO NOT inflate confidence. A LOW-confidence honest answer is MORE VALUABLE than a HIGH-confidence wrong answer. The architect routes decisions based on your confidence level.
40948
+
40727
40949
  ## RESEARCH DEPTH & CONFIDENCE
40728
40950
  State confidence level with EVERY finding:
40729
40951
  - HIGH: verified from multiple sources or direct documentation
@@ -40734,7 +40956,8 @@ State confidence level with EVERY finding:
40734
40956
  If returning cached result, check cachedAt timestamp against TTL. If approaching TTL, flag as STALE_RISK.
40735
40957
 
40736
40958
  ## SCOPE BOUNDARY
40737
- You research and report. You do NOT recommend implementation approaches, architect decisions, or code patterns. Those are the Architect's domain.
40959
+ You research and report. You MAY recommend domain-specific approaches, APIs, constraints, and trade-offs that the implementation should follow.
40960
+ You do NOT make final architecture decisions, choose product scope, or write code. Those are the Architect's and Coder's domains.
40738
40961
 
40739
40962
  ## PLATFORM AWARENESS
40740
40963
  When researching file system operations, Node.js APIs, path handling, process management, or any OS-interaction pattern, explicitly verify cross-platform compatibility (Windows, macOS, Linux). Flag any API where behavior differs across platforms (e.g., fs.renameSync cannot atomically overwrite existing directories on Windows).
@@ -40747,7 +40970,9 @@ TASK: [what guidance is needed]
40747
40970
  DOMAIN: [the domain - e.g., security, ios, android, rust, kubernetes]
40748
40971
  INPUT: [context/requirements]
40749
40972
 
40750
- ## OUTPUT FORMAT
40973
+ ## OUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected)
40974
+ Begin directly with CONFIDENCE. Do NOT prepend "Here's my research..." or any conversational preamble.
40975
+
40751
40976
  CONFIDENCE: HIGH | MEDIUM | LOW
40752
40977
  CRITICAL: [key domain-specific considerations]
40753
40978
  APPROACH: [recommended implementation approach]
@@ -40770,15 +40995,6 @@ Before fetching URL, check .swarm/context.md for ## Research Sources.
40770
40995
  - Cache bypass: if user requests fresh research
40771
40996
  - SME is read-only. Cache persistence is Architect's responsibility.
40772
40997
 
40773
- ROLE-RELEVANCE TAGGING
40774
- When writing output consumed by other agents, prefix with:
40775
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40776
- [FOR: ALL] \u2014 relevant to all agents
40777
- Examples:
40778
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40779
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40780
- [FOR: ALL] "Breaking change: StateManager renamed"
40781
- This tag is informational in v6.19; v6.20 will use for context filtering.
40782
40998
  `;
40783
40999
  function createSMEAgent(model, customPrompt, customAppendPrompt) {
40784
41000
  let prompt = SME_PROMPT;
@@ -40875,27 +41091,79 @@ SECURITY GUIDANCE (MANDATORY):
40875
41091
  - SANITIZE sensitive absolute paths and stack traces before reporting (replace with [REDACTED] or generic paths)
40876
41092
  - Apply redaction to any failure output that may contain credentials, keys, tokens, or sensitive system paths
40877
41093
 
40878
- OUTPUT FORMAT:
40879
- VERDICT: PASS | FAIL
41094
+ ## ASSERTION QUALITY RULES
41095
+
41096
+ ### BANNED \u2014 These are test theater. NEVER use:
41097
+ - \`expect(result).toBeTruthy()\` \u2014 USE: \`expect(result).toBe(specificValue)\`
41098
+ - \`expect(result).toBeDefined()\` \u2014 USE: \`expect(result).toEqual(expectedShape)\`
41099
+ - \`expect(array).toBeInstanceOf(Array)\` \u2014 USE: \`expect(array).toEqual([specific, items])\`
41100
+ - \`expect(fn).not.toThrow()\` alone \u2014 USE: \`expect(fn()).toBe(expectedReturn)\`
41101
+ - Tests that only check "it doesn't crash" \u2014 that is not a test, it is hope
41102
+
41103
+ ### REQUIRED \u2014 Every test MUST have at least one of:
41104
+ 1. EXACT VALUE: \`expect(result).toBe(42)\` or \`expect(result).toEqual({specific: 'shape'})\`
41105
+ 2. STATE CHANGE: \`expect(countAfter - countBefore).toBe(1)\`
41106
+ 3. ERROR WITH MESSAGE: \`expect(() => fn()).toThrow('specific message')\`
41107
+ 4. CALL VERIFICATION: \`expect(mock).toHaveBeenCalledWith(specific, args)\`
41108
+
41109
+ ### TEST STRUCTURE \u2014 Every test file MUST include:
41110
+ 1. HAPPY PATH: Normal inputs \u2192 expected exact output values
41111
+ 2. ERROR PATH: Invalid inputs \u2192 specific error behavior
41112
+ 3. BOUNDARY: Empty input, null/undefined, max values, Unicode, special characters
41113
+ 4. STATE MUTATION: If function modifies state, assert the value before AND after
41114
+
41115
+ ## PROPERTY-BASED TESTING
41116
+
41117
+ For functions with mathematical or logical properties, define INVARIANTS rather than only example-based tests:
41118
+ - IDEMPOTENCY: f(f(x)) === f(x) for operations that should be stable
41119
+ - ROUND-TRIP: decode(encode(x)) === x for serialization
41120
+ - MONOTONICITY: if a < b then f(a) <= f(b) for sorting/ordering
41121
+ - PRESERVATION: output.length === input.length for transformations
41122
+
41123
+ Property tests are MORE VALUABLE than example tests because they:
41124
+ 1. Test invariants the code author might not have considered
41125
+ 2. Use varied inputs that bypass confirmation bias
41126
+ 3. Catch edge cases that hand-picked examples miss
41127
+
41128
+ When a function has a clear mathematical property, write at least one property-based test alongside your example tests.
41129
+
41130
+ ## SELF-REVIEW (mandatory before reporting verdict)
41131
+
41132
+ Before reporting your VERDICT, run this checklist:
41133
+ 1. Re-read the SOURCE file being tested
41134
+ 2. Count the public functions/methods/exports
41135
+ 3. Confirm EVERY public function has at least one test
41136
+ 4. Confirm every test has at least one EXACT VALUE assertion (not toBeTruthy/toBeDefined)
41137
+ 5. If any gap: write the missing test before reporting
41138
+
41139
+ COVERAGE FLOOR: If you tested fewer than 80% of public functions, report:
41140
+ INCOMPLETE \u2014 [N] of [M] public functions tested. Missing: [list of untested functions]
41141
+ Do NOT report PASS/FAIL until coverage is at least 80%.
41142
+
41143
+ ## EXECUTION VERIFICATION
41144
+
41145
+ After writing tests, you MUST run them. A test file that was written but never executed is NOT a deliverable.
41146
+
41147
+ When tests fail:
41148
+ - FIRST: Check if the failure reveals a bug in the SOURCE code (this is a GOOD outcome \u2014 report it)
41149
+ - SECOND: Check if the failure reveals a bug in your TEST (fix the test)
41150
+ - NEVER: Weaken assertions to make tests pass (e.g., changing toBe(42) to toBeTruthy())
41151
+ Weakening assertions to pass is the definition of test theater.
41152
+
41153
+ OUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected):
41154
+ Begin directly with the VERDICT line. Do NOT prepend "Here's my analysis..." or any conversational preamble.
41155
+
41156
+ VERDICT: PASS [N/N tests passed] | FAIL [N passed, M failed]
40880
41157
  TESTS: [total count] tests, [pass count] passed, [fail count] failed
40881
41158
  FAILURES: [list of failed test names + error messages, if any]
40882
- COVERAGE: [areas covered]
41159
+ COVERAGE: [X]% of public functions \u2014 [areas covered]
41160
+ BUGS FOUND: [list any source code bugs discovered during testing, or "none"]
40883
41161
 
40884
41162
  COVERAGE REPORTING:
40885
41163
  - After running tests, report the line/branch coverage percentage if the test runner provides it.
40886
41164
  - Format: COVERAGE_PCT: [N]% (or "N/A" if not available)
40887
41165
  - If COVERAGE_PCT < 70%, add a note: "COVERAGE_WARNING: Below 70% threshold \u2014 consider additional test cases for uncovered paths."
40888
41166
  - The architect uses this to decide whether to request an additional test pass (Rule 10 / Phase 5 step 5h).
40889
-
40890
- ROLE-RELEVANCE TAGGING
40891
- When writing output consumed by other agents, prefix with:
40892
- [FOR: agent1, agent2] \u2014 relevant to specific agents
40893
- [FOR: ALL] \u2014 relevant to all agents
40894
- Examples:
40895
- [FOR: reviewer, test_engineer] "Added validation \u2014 needs safety check"
40896
- [FOR: architect] "Research: Tree-sitter supports TypeScript AST"
40897
- [FOR: ALL] "Breaking change: StateManager renamed"
40898
- This tag is informational in v6.19; v6.20 will use for context filtering.
40899
41167
  `;
40900
41168
  function createTestEngineerAgent(model, customPrompt, customAppendPrompt) {
40901
41169
  let prompt = TEST_ENGINEER_PROMPT;
@@ -48774,6 +49042,28 @@ function createDelegationGateHook(config3) {
48774
49042
  }
48775
49043
  }
48776
49044
  }
49045
+ if (typeof subagentType === "string") {
49046
+ const evidenceTaskId = session.currentTaskId ?? session.lastCoderDelegationTaskId;
49047
+ if (evidenceTaskId) {
49048
+ try {
49049
+ const gateAgents = [
49050
+ "reviewer",
49051
+ "test_engineer",
49052
+ "docs",
49053
+ "designer",
49054
+ "critic"
49055
+ ];
49056
+ const targetAgentForEvidence = stripKnownSwarmPrefix(subagentType);
49057
+ if (gateAgents.includes(targetAgentForEvidence)) {
49058
+ const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
49059
+ await recordGateEvidence2(process.cwd(), evidenceTaskId, targetAgentForEvidence, input.sessionID);
49060
+ } else {
49061
+ const { recordAgentDispatch: recordAgentDispatch2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
49062
+ await recordAgentDispatch2(process.cwd(), evidenceTaskId, targetAgentForEvidence);
49063
+ }
49064
+ } catch {}
49065
+ }
49066
+ }
48777
49067
  if (storedArgs !== undefined) {
48778
49068
  deleteStoredInputArgs(input.callID);
48779
49069
  }
@@ -48866,6 +49156,21 @@ function createDelegationGateHook(config3) {
48866
49156
  }
48867
49157
  }
48868
49158
  }
49159
+ {
49160
+ const evidenceTaskId = session.currentTaskId ?? session.lastCoderDelegationTaskId;
49161
+ if (evidenceTaskId) {
49162
+ try {
49163
+ if (hasReviewer) {
49164
+ const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
49165
+ await recordGateEvidence2(process.cwd(), evidenceTaskId, "reviewer", input.sessionID);
49166
+ }
49167
+ if (hasTestEngineer) {
49168
+ const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
49169
+ await recordGateEvidence2(process.cwd(), evidenceTaskId, "test_engineer", input.sessionID);
49170
+ }
49171
+ } catch {}
49172
+ }
49173
+ }
48869
49174
  }
48870
49175
  }
48871
49176
  };
@@ -49057,11 +49362,11 @@ ${trimComment}${after}`;
49057
49362
  if (!hasReviewer || !hasTestEngineer || priorTaskStuckAtCoder) {
49058
49363
  if (session.qaSkipCount >= 1) {
49059
49364
  const skippedTasks = session.qaSkipTaskIds.join(", ");
49060
- throw new Error(`\uD83D\uDED1 QA GATE ENFORCEMENT: ${session.qaSkipCount + 1} consecutive coder delegations without reviewer/test_engineer. ` + `Skipped tasks: [${skippedTasks}]. ` + `DELEGATE to reviewer and test_engineer NOW before any further coder work.`);
49365
+ throw new Error(`\uD83D\uDED1 QA GATE ENFORCEMENT: ${session.qaSkipCount + 1} consecutive coder delegations without reviewer/test_engineer. Skipped tasks: [${skippedTasks}]. DELEGATE to reviewer and test_engineer NOW before any further coder work.`);
49061
49366
  }
49062
49367
  session.qaSkipCount++;
49063
49368
  session.qaSkipTaskIds.push(currentTaskId ?? "unknown");
49064
- warnings.push(`\u26A0\uFE0F PROTOCOL VIOLATION: Previous coder task completed, but QA gate was skipped. ` + `You MUST delegate to reviewer (code review) and test_engineer (test execution) ` + `before starting a new coder task. Review RULES 7-8 in your system prompt.`);
49369
+ warnings.push(`\u26A0\uFE0F PROTOCOL VIOLATION: Previous coder task completed, but QA gate was skipped. ` + `You MUST delegate to reviewer (code review) and test_engineer (test execution) before starting a new coder task. Review RULES 7-8 in your system prompt.`);
49065
49370
  }
49066
49371
  }
49067
49372
  }
@@ -49413,7 +49718,7 @@ import * as fs16 from "fs";
49413
49718
  init_utils2();
49414
49719
  init_manager2();
49415
49720
  import * as fs15 from "fs";
49416
- import * as path27 from "path";
49721
+ import * as path28 from "path";
49417
49722
  var DEFAULT_DRIFT_CONFIG = {
49418
49723
  staleThresholdPhases: 1,
49419
49724
  detectContradictions: true,
@@ -49567,7 +49872,7 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
49567
49872
  currentPhase = legacyPhase;
49568
49873
  }
49569
49874
  }
49570
- const contextPath = path27.join(directory, ".swarm", "context.md");
49875
+ const contextPath = path28.join(directory, ".swarm", "context.md");
49571
49876
  let contextContent = "";
49572
49877
  try {
49573
49878
  if (fs15.existsSync(contextPath)) {
@@ -51190,7 +51495,7 @@ function createDarkMatterDetectorHook(directory) {
51190
51495
  // src/hooks/knowledge-reader.ts
51191
51496
  import { existsSync as existsSync18 } from "fs";
51192
51497
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
51193
- import * as path28 from "path";
51498
+ import * as path29 from "path";
51194
51499
  var JACCARD_THRESHOLD = 0.6;
51195
51500
  var HIVE_TIER_BOOST = 0.05;
51196
51501
  var SAME_PROJECT_PENALTY = -0.05;
@@ -51238,7 +51543,7 @@ function inferCategoriesFromPhase(phaseDescription) {
51238
51543
  return ["process", "tooling"];
51239
51544
  }
51240
51545
  async function recordLessonsShown(directory, lessonIds, currentPhase) {
51241
- const shownFile = path28.join(directory, ".swarm", ".knowledge-shown.json");
51546
+ const shownFile = path29.join(directory, ".swarm", ".knowledge-shown.json");
51242
51547
  try {
51243
51548
  let shownData = {};
51244
51549
  if (existsSync18(shownFile)) {
@@ -51246,7 +51551,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
51246
51551
  shownData = JSON.parse(content);
51247
51552
  }
51248
51553
  shownData[currentPhase] = lessonIds;
51249
- await mkdir4(path28.dirname(shownFile), { recursive: true });
51554
+ await mkdir4(path29.dirname(shownFile), { recursive: true });
51250
51555
  await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
51251
51556
  } catch {
51252
51557
  console.warn("[swarm] Knowledge: failed to record shown lessons");
@@ -51341,7 +51646,7 @@ async function readMergedKnowledge(directory, config3, context) {
51341
51646
  return topN;
51342
51647
  }
51343
51648
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
51344
- const shownFile = path28.join(directory, ".swarm", ".knowledge-shown.json");
51649
+ const shownFile = path29.join(directory, ".swarm", ".knowledge-shown.json");
51345
51650
  try {
51346
51651
  if (!existsSync18(shownFile)) {
51347
51652
  return;
@@ -51814,10 +52119,10 @@ Use this data to avoid repeating known failure patterns.`;
51814
52119
  init_event_bus();
51815
52120
  init_utils2();
51816
52121
  import * as fs17 from "fs";
51817
- import * as path29 from "path";
52122
+ import * as path30 from "path";
51818
52123
  var DRIFT_REPORT_PREFIX = "drift-report-phase-";
51819
52124
  async function readPriorDriftReports(directory) {
51820
- const swarmDir = path29.join(directory, ".swarm");
52125
+ const swarmDir = path30.join(directory, ".swarm");
51821
52126
  const entries = await fs17.promises.readdir(swarmDir).catch(() => null);
51822
52127
  if (entries === null)
51823
52128
  return [];
@@ -51844,7 +52149,7 @@ async function readPriorDriftReports(directory) {
51844
52149
  async function writeDriftReport(directory, report) {
51845
52150
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
51846
52151
  const filePath = validateSwarmPath(directory, filename);
51847
- const swarmDir = path29.dirname(filePath);
52152
+ const swarmDir = path30.dirname(filePath);
51848
52153
  await fs17.promises.mkdir(swarmDir, { recursive: true });
51849
52154
  try {
51850
52155
  await fs17.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
@@ -52153,7 +52458,7 @@ init_config_doctor();
52153
52458
 
52154
52459
  // src/session/snapshot-reader.ts
52155
52460
  init_utils2();
52156
- import path30 from "path";
52461
+ import path31 from "path";
52157
52462
  var VALID_TASK_WORKFLOW_STATES = [
52158
52463
  "idle",
52159
52464
  "coder_delegated",
@@ -52276,7 +52581,7 @@ function rehydrateState(snapshot) {
52276
52581
  async function reconcileTaskStatesFromPlan(directory) {
52277
52582
  let raw;
52278
52583
  try {
52279
- raw = await Bun.file(path30.join(directory, ".swarm/plan.json")).text();
52584
+ raw = await Bun.file(path31.join(directory, ".swarm/plan.json")).text();
52280
52585
  } catch {
52281
52586
  return;
52282
52587
  }
@@ -52500,7 +52805,7 @@ init_tool();
52500
52805
  init_create_tool();
52501
52806
  import { spawnSync } from "child_process";
52502
52807
  import * as fs19 from "fs";
52503
- import * as path31 from "path";
52808
+ import * as path32 from "path";
52504
52809
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
52505
52810
  var MAX_LABEL_LENGTH = 100;
52506
52811
  var GIT_TIMEOUT_MS = 30000;
@@ -52551,7 +52856,7 @@ function validateLabel(label) {
52551
52856
  return null;
52552
52857
  }
52553
52858
  function getCheckpointLogPath(directory) {
52554
- return path31.join(directory, CHECKPOINT_LOG_PATH);
52859
+ return path32.join(directory, CHECKPOINT_LOG_PATH);
52555
52860
  }
52556
52861
  function readCheckpointLog(directory) {
52557
52862
  const logPath = getCheckpointLogPath(directory);
@@ -52569,7 +52874,7 @@ function readCheckpointLog(directory) {
52569
52874
  }
52570
52875
  function writeCheckpointLog(log2, directory) {
52571
52876
  const logPath = getCheckpointLogPath(directory);
52572
- const dir = path31.dirname(logPath);
52877
+ const dir = path32.dirname(logPath);
52573
52878
  if (!fs19.existsSync(dir)) {
52574
52879
  fs19.mkdirSync(dir, { recursive: true });
52575
52880
  }
@@ -52777,7 +53082,7 @@ var checkpoint = createSwarmTool({
52777
53082
  init_dist();
52778
53083
  init_create_tool();
52779
53084
  import * as fs20 from "fs";
52780
- import * as path32 from "path";
53085
+ import * as path33 from "path";
52781
53086
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
52782
53087
  var DEFAULT_DAYS = 90;
52783
53088
  var DEFAULT_TOP_N = 20;
@@ -52921,7 +53226,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
52921
53226
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
52922
53227
  const filteredChurn = new Map;
52923
53228
  for (const [file3, count] of churnMap) {
52924
- const ext = path32.extname(file3).toLowerCase();
53229
+ const ext = path33.extname(file3).toLowerCase();
52925
53230
  if (extSet.has(ext)) {
52926
53231
  filteredChurn.set(file3, count);
52927
53232
  }
@@ -52932,7 +53237,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
52932
53237
  for (const [file3, churnCount] of filteredChurn) {
52933
53238
  let fullPath = file3;
52934
53239
  if (!fs20.existsSync(fullPath)) {
52935
- fullPath = path32.join(cwd, file3);
53240
+ fullPath = path33.join(cwd, file3);
52936
53241
  }
52937
53242
  const complexity = getComplexityForFile(fullPath);
52938
53243
  if (complexity !== null) {
@@ -53080,7 +53385,7 @@ var complexity_hotspots = createSwarmTool({
53080
53385
  // src/tools/declare-scope.ts
53081
53386
  init_tool();
53082
53387
  import * as fs21 from "fs";
53083
- import * as path33 from "path";
53388
+ import * as path34 from "path";
53084
53389
  init_create_tool();
53085
53390
  function validateTaskIdFormat(taskId) {
53086
53391
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -53159,8 +53464,8 @@ async function executeDeclareScope(args2, fallbackDir) {
53159
53464
  };
53160
53465
  }
53161
53466
  }
53162
- normalizedDir = path33.normalize(args2.working_directory);
53163
- const pathParts = normalizedDir.split(path33.sep);
53467
+ normalizedDir = path34.normalize(args2.working_directory);
53468
+ const pathParts = normalizedDir.split(path34.sep);
53164
53469
  if (pathParts.includes("..")) {
53165
53470
  return {
53166
53471
  success: false,
@@ -53170,10 +53475,10 @@ async function executeDeclareScope(args2, fallbackDir) {
53170
53475
  ]
53171
53476
  };
53172
53477
  }
53173
- const resolvedDir = path33.resolve(normalizedDir);
53478
+ const resolvedDir = path34.resolve(normalizedDir);
53174
53479
  try {
53175
53480
  const realPath = fs21.realpathSync(resolvedDir);
53176
- const planPath2 = path33.join(realPath, ".swarm", "plan.json");
53481
+ const planPath2 = path34.join(realPath, ".swarm", "plan.json");
53177
53482
  if (!fs21.existsSync(planPath2)) {
53178
53483
  return {
53179
53484
  success: false,
@@ -53194,7 +53499,7 @@ async function executeDeclareScope(args2, fallbackDir) {
53194
53499
  }
53195
53500
  }
53196
53501
  const directory = normalizedDir ?? fallbackDir ?? process.cwd();
53197
- const planPath = path33.resolve(directory, ".swarm", "plan.json");
53502
+ const planPath = path34.resolve(directory, ".swarm", "plan.json");
53198
53503
  if (!fs21.existsSync(planPath)) {
53199
53504
  return {
53200
53505
  success: false,
@@ -53284,20 +53589,20 @@ function validateBase(base) {
53284
53589
  function validatePaths(paths) {
53285
53590
  if (!paths)
53286
53591
  return null;
53287
- for (const path34 of paths) {
53288
- if (!path34 || path34.length === 0) {
53592
+ for (const path35 of paths) {
53593
+ if (!path35 || path35.length === 0) {
53289
53594
  return "empty path not allowed";
53290
53595
  }
53291
- if (path34.length > MAX_PATH_LENGTH) {
53596
+ if (path35.length > MAX_PATH_LENGTH) {
53292
53597
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
53293
53598
  }
53294
- if (SHELL_METACHARACTERS2.test(path34)) {
53599
+ if (SHELL_METACHARACTERS2.test(path35)) {
53295
53600
  return "path contains shell metacharacters";
53296
53601
  }
53297
- if (path34.startsWith("-")) {
53602
+ if (path35.startsWith("-")) {
53298
53603
  return 'path cannot start with "-" (option-like arguments not allowed)';
53299
53604
  }
53300
- if (CONTROL_CHAR_PATTERN2.test(path34)) {
53605
+ if (CONTROL_CHAR_PATTERN2.test(path35)) {
53301
53606
  return "path contains control characters";
53302
53607
  }
53303
53608
  }
@@ -53377,8 +53682,8 @@ var diff = tool({
53377
53682
  if (parts2.length >= 3) {
53378
53683
  const additions = parseInt(parts2[0], 10) || 0;
53379
53684
  const deletions = parseInt(parts2[1], 10) || 0;
53380
- const path34 = parts2[2];
53381
- files.push({ path: path34, additions, deletions });
53685
+ const path35 = parts2[2];
53686
+ files.push({ path: path35, additions, deletions });
53382
53687
  }
53383
53688
  }
53384
53689
  const contractChanges = [];
@@ -53608,7 +53913,7 @@ Use these as DOMAIN values when delegating to @sme.`;
53608
53913
  init_dist();
53609
53914
  init_create_tool();
53610
53915
  import * as fs22 from "fs";
53611
- import * as path34 from "path";
53916
+ import * as path35 from "path";
53612
53917
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
53613
53918
  var MAX_EVIDENCE_FILES = 1000;
53614
53919
  var EVIDENCE_DIR = ".swarm/evidence";
@@ -53631,9 +53936,9 @@ function validateRequiredTypes(input) {
53631
53936
  return null;
53632
53937
  }
53633
53938
  function isPathWithinSwarm(filePath, cwd) {
53634
- const normalizedCwd = path34.resolve(cwd);
53635
- const swarmPath = path34.join(normalizedCwd, ".swarm");
53636
- const normalizedPath = path34.resolve(filePath);
53939
+ const normalizedCwd = path35.resolve(cwd);
53940
+ const swarmPath = path35.join(normalizedCwd, ".swarm");
53941
+ const normalizedPath = path35.resolve(filePath);
53637
53942
  return normalizedPath.startsWith(swarmPath);
53638
53943
  }
53639
53944
  function parseCompletedTasks(planContent) {
@@ -53663,10 +53968,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
53663
53968
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
53664
53969
  continue;
53665
53970
  }
53666
- const filePath = path34.join(evidenceDir, filename);
53971
+ const filePath = path35.join(evidenceDir, filename);
53667
53972
  try {
53668
- const resolvedPath = path34.resolve(filePath);
53669
- const evidenceDirResolved = path34.resolve(evidenceDir);
53973
+ const resolvedPath = path35.resolve(filePath);
53974
+ const evidenceDirResolved = path35.resolve(evidenceDir);
53670
53975
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
53671
53976
  continue;
53672
53977
  }
@@ -53773,7 +54078,7 @@ var evidence_check = createSwarmTool({
53773
54078
  return JSON.stringify(errorResult, null, 2);
53774
54079
  }
53775
54080
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
53776
- const planPath = path34.join(cwd, PLAN_FILE);
54081
+ const planPath = path35.join(cwd, PLAN_FILE);
53777
54082
  if (!isPathWithinSwarm(planPath, cwd)) {
53778
54083
  const errorResult = {
53779
54084
  error: "plan file path validation failed",
@@ -53805,7 +54110,7 @@ var evidence_check = createSwarmTool({
53805
54110
  };
53806
54111
  return JSON.stringify(result2, null, 2);
53807
54112
  }
53808
- const evidenceDir = path34.join(cwd, EVIDENCE_DIR);
54113
+ const evidenceDir = path35.join(cwd, EVIDENCE_DIR);
53809
54114
  const evidence = readEvidenceFiles(evidenceDir, cwd);
53810
54115
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
53811
54116
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -53823,7 +54128,7 @@ var evidence_check = createSwarmTool({
53823
54128
  init_tool();
53824
54129
  init_create_tool();
53825
54130
  import * as fs23 from "fs";
53826
- import * as path35 from "path";
54131
+ import * as path36 from "path";
53827
54132
  var EXT_MAP = {
53828
54133
  python: ".py",
53829
54134
  py: ".py",
@@ -53904,12 +54209,12 @@ var extract_code_blocks = createSwarmTool({
53904
54209
  if (prefix) {
53905
54210
  filename = `${prefix}_${filename}`;
53906
54211
  }
53907
- let filepath = path35.join(targetDir, filename);
53908
- const base = path35.basename(filepath, path35.extname(filepath));
53909
- const ext = path35.extname(filepath);
54212
+ let filepath = path36.join(targetDir, filename);
54213
+ const base = path36.basename(filepath, path36.extname(filepath));
54214
+ const ext = path36.extname(filepath);
53910
54215
  let counter = 1;
53911
54216
  while (fs23.existsSync(filepath)) {
53912
- filepath = path35.join(targetDir, `${base}_${counter}${ext}`);
54217
+ filepath = path36.join(targetDir, `${base}_${counter}${ext}`);
53913
54218
  counter++;
53914
54219
  }
53915
54220
  try {
@@ -54027,7 +54332,7 @@ var gitingest = tool({
54027
54332
  // src/tools/imports.ts
54028
54333
  init_dist();
54029
54334
  import * as fs24 from "fs";
54030
- import * as path36 from "path";
54335
+ import * as path37 from "path";
54031
54336
  var MAX_FILE_PATH_LENGTH2 = 500;
54032
54337
  var MAX_SYMBOL_LENGTH = 256;
54033
54338
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -54081,7 +54386,7 @@ function validateSymbolInput(symbol3) {
54081
54386
  return null;
54082
54387
  }
54083
54388
  function isBinaryFile2(filePath, buffer) {
54084
- const ext = path36.extname(filePath).toLowerCase();
54389
+ const ext = path37.extname(filePath).toLowerCase();
54085
54390
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
54086
54391
  return false;
54087
54392
  }
@@ -54105,15 +54410,15 @@ function parseImports(content, targetFile, targetSymbol) {
54105
54410
  const imports = [];
54106
54411
  let _resolvedTarget;
54107
54412
  try {
54108
- _resolvedTarget = path36.resolve(targetFile);
54413
+ _resolvedTarget = path37.resolve(targetFile);
54109
54414
  } catch {
54110
54415
  _resolvedTarget = targetFile;
54111
54416
  }
54112
- const targetBasename = path36.basename(targetFile, path36.extname(targetFile));
54417
+ const targetBasename = path37.basename(targetFile, path37.extname(targetFile));
54113
54418
  const targetWithExt = targetFile;
54114
54419
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
54115
- const normalizedTargetWithExt = path36.normalize(targetWithExt).replace(/\\/g, "/");
54116
- const normalizedTargetWithoutExt = path36.normalize(targetWithoutExt).replace(/\\/g, "/");
54420
+ const normalizedTargetWithExt = path37.normalize(targetWithExt).replace(/\\/g, "/");
54421
+ const normalizedTargetWithoutExt = path37.normalize(targetWithoutExt).replace(/\\/g, "/");
54117
54422
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
54118
54423
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
54119
54424
  const modulePath = match[1] || match[2] || match[3];
@@ -54136,9 +54441,9 @@ function parseImports(content, targetFile, targetSymbol) {
54136
54441
  }
54137
54442
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
54138
54443
  let isMatch = false;
54139
- const _targetDir = path36.dirname(targetFile);
54140
- const targetExt = path36.extname(targetFile);
54141
- const targetBasenameNoExt = path36.basename(targetFile, targetExt);
54444
+ const _targetDir = path37.dirname(targetFile);
54445
+ const targetExt = path37.extname(targetFile);
54446
+ const targetBasenameNoExt = path37.basename(targetFile, targetExt);
54142
54447
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
54143
54448
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
54144
54449
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -54206,10 +54511,10 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
54206
54511
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
54207
54512
  for (const entry of entries) {
54208
54513
  if (SKIP_DIRECTORIES2.has(entry)) {
54209
- stats.skippedDirs.push(path36.join(dir, entry));
54514
+ stats.skippedDirs.push(path37.join(dir, entry));
54210
54515
  continue;
54211
54516
  }
54212
- const fullPath = path36.join(dir, entry);
54517
+ const fullPath = path37.join(dir, entry);
54213
54518
  let stat2;
54214
54519
  try {
54215
54520
  stat2 = fs24.statSync(fullPath);
@@ -54223,7 +54528,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
54223
54528
  if (stat2.isDirectory()) {
54224
54529
  findSourceFiles2(fullPath, files, stats);
54225
54530
  } else if (stat2.isFile()) {
54226
- const ext = path36.extname(fullPath).toLowerCase();
54531
+ const ext = path37.extname(fullPath).toLowerCase();
54227
54532
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
54228
54533
  files.push(fullPath);
54229
54534
  }
@@ -54279,7 +54584,7 @@ var imports = tool({
54279
54584
  return JSON.stringify(errorResult, null, 2);
54280
54585
  }
54281
54586
  try {
54282
- const targetFile = path36.resolve(file3);
54587
+ const targetFile = path37.resolve(file3);
54283
54588
  if (!fs24.existsSync(targetFile)) {
54284
54589
  const errorResult = {
54285
54590
  error: `target file not found: ${file3}`,
@@ -54301,7 +54606,7 @@ var imports = tool({
54301
54606
  };
54302
54607
  return JSON.stringify(errorResult, null, 2);
54303
54608
  }
54304
- const baseDir = path36.dirname(targetFile);
54609
+ const baseDir = path37.dirname(targetFile);
54305
54610
  const scanStats = {
54306
54611
  skippedDirs: [],
54307
54612
  skippedFiles: 0,
@@ -54623,7 +54928,7 @@ init_config();
54623
54928
  init_schema();
54624
54929
  init_manager();
54625
54930
  import * as fs25 from "fs";
54626
- import * as path37 from "path";
54931
+ import * as path38 from "path";
54627
54932
  init_utils2();
54628
54933
  init_create_tool();
54629
54934
  function safeWarn(message, error93) {
@@ -54818,7 +55123,7 @@ async function executePhaseComplete(args2, workingDirectory) {
54818
55123
  }
54819
55124
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
54820
55125
  try {
54821
- const projectName = path37.basename(dir);
55126
+ const projectName = path38.basename(dir);
54822
55127
  const knowledgeConfig = {
54823
55128
  enabled: true,
54824
55129
  swarm_max_entries: 100,
@@ -54982,7 +55287,7 @@ init_discovery();
54982
55287
  init_utils();
54983
55288
  init_create_tool();
54984
55289
  import * as fs26 from "fs";
54985
- import * as path38 from "path";
55290
+ import * as path39 from "path";
54986
55291
  var MAX_OUTPUT_BYTES5 = 52428800;
54987
55292
  var AUDIT_TIMEOUT_MS = 120000;
54988
55293
  function isValidEcosystem(value) {
@@ -55000,16 +55305,16 @@ function validateArgs3(args2) {
55000
55305
  function detectEcosystems(directory) {
55001
55306
  const ecosystems = [];
55002
55307
  const cwd = directory;
55003
- if (fs26.existsSync(path38.join(cwd, "package.json"))) {
55308
+ if (fs26.existsSync(path39.join(cwd, "package.json"))) {
55004
55309
  ecosystems.push("npm");
55005
55310
  }
55006
- if (fs26.existsSync(path38.join(cwd, "pyproject.toml")) || fs26.existsSync(path38.join(cwd, "requirements.txt"))) {
55311
+ if (fs26.existsSync(path39.join(cwd, "pyproject.toml")) || fs26.existsSync(path39.join(cwd, "requirements.txt"))) {
55007
55312
  ecosystems.push("pip");
55008
55313
  }
55009
- if (fs26.existsSync(path38.join(cwd, "Cargo.toml"))) {
55314
+ if (fs26.existsSync(path39.join(cwd, "Cargo.toml"))) {
55010
55315
  ecosystems.push("cargo");
55011
55316
  }
55012
- if (fs26.existsSync(path38.join(cwd, "go.mod"))) {
55317
+ if (fs26.existsSync(path39.join(cwd, "go.mod"))) {
55013
55318
  ecosystems.push("go");
55014
55319
  }
55015
55320
  try {
@@ -55018,10 +55323,10 @@ function detectEcosystems(directory) {
55018
55323
  ecosystems.push("dotnet");
55019
55324
  }
55020
55325
  } catch {}
55021
- if (fs26.existsSync(path38.join(cwd, "Gemfile")) || fs26.existsSync(path38.join(cwd, "Gemfile.lock"))) {
55326
+ if (fs26.existsSync(path39.join(cwd, "Gemfile")) || fs26.existsSync(path39.join(cwd, "Gemfile.lock"))) {
55022
55327
  ecosystems.push("ruby");
55023
55328
  }
55024
- if (fs26.existsSync(path38.join(cwd, "pubspec.yaml"))) {
55329
+ if (fs26.existsSync(path39.join(cwd, "pubspec.yaml"))) {
55025
55330
  ecosystems.push("dart");
55026
55331
  }
55027
55332
  return ecosystems;
@@ -56084,7 +56389,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
56084
56389
  // src/tools/pre-check-batch.ts
56085
56390
  init_dist();
56086
56391
  import * as fs29 from "fs";
56087
- import * as path41 from "path";
56392
+ import * as path42 from "path";
56088
56393
 
56089
56394
  // node_modules/yocto-queue/index.js
56090
56395
  class Node2 {
@@ -56252,7 +56557,7 @@ init_manager();
56252
56557
 
56253
56558
  // src/quality/metrics.ts
56254
56559
  import * as fs27 from "fs";
56255
- import * as path39 from "path";
56560
+ import * as path40 from "path";
56256
56561
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
56257
56562
  var MIN_DUPLICATION_LINES = 10;
56258
56563
  function estimateCyclomaticComplexity(content) {
@@ -56304,7 +56609,7 @@ async function computeComplexityDelta(files, workingDir) {
56304
56609
  let totalComplexity = 0;
56305
56610
  const analyzedFiles = [];
56306
56611
  for (const file3 of files) {
56307
- const fullPath = path39.isAbsolute(file3) ? file3 : path39.join(workingDir, file3);
56612
+ const fullPath = path40.isAbsolute(file3) ? file3 : path40.join(workingDir, file3);
56308
56613
  if (!fs27.existsSync(fullPath)) {
56309
56614
  continue;
56310
56615
  }
@@ -56427,7 +56732,7 @@ function countGoExports(content) {
56427
56732
  function getExportCountForFile(filePath) {
56428
56733
  try {
56429
56734
  const content = fs27.readFileSync(filePath, "utf-8");
56430
- const ext = path39.extname(filePath).toLowerCase();
56735
+ const ext = path40.extname(filePath).toLowerCase();
56431
56736
  switch (ext) {
56432
56737
  case ".ts":
56433
56738
  case ".tsx":
@@ -56453,7 +56758,7 @@ async function computePublicApiDelta(files, workingDir) {
56453
56758
  let totalExports = 0;
56454
56759
  const analyzedFiles = [];
56455
56760
  for (const file3 of files) {
56456
- const fullPath = path39.isAbsolute(file3) ? file3 : path39.join(workingDir, file3);
56761
+ const fullPath = path40.isAbsolute(file3) ? file3 : path40.join(workingDir, file3);
56457
56762
  if (!fs27.existsSync(fullPath)) {
56458
56763
  continue;
56459
56764
  }
@@ -56487,7 +56792,7 @@ async function computeDuplicationRatio(files, workingDir) {
56487
56792
  let duplicateLines = 0;
56488
56793
  const analyzedFiles = [];
56489
56794
  for (const file3 of files) {
56490
- const fullPath = path39.isAbsolute(file3) ? file3 : path39.join(workingDir, file3);
56795
+ const fullPath = path40.isAbsolute(file3) ? file3 : path40.join(workingDir, file3);
56491
56796
  if (!fs27.existsSync(fullPath)) {
56492
56797
  continue;
56493
56798
  }
@@ -56520,8 +56825,8 @@ function countCodeLines(content) {
56520
56825
  return lines.length;
56521
56826
  }
56522
56827
  function isTestFile(filePath) {
56523
- const basename8 = path39.basename(filePath);
56524
- const _ext = path39.extname(filePath).toLowerCase();
56828
+ const basename8 = path40.basename(filePath);
56829
+ const _ext = path40.extname(filePath).toLowerCase();
56525
56830
  const testPatterns = [
56526
56831
  ".test.",
56527
56832
  ".spec.",
@@ -56602,8 +56907,8 @@ function matchGlobSegment(globSegments, pathSegments) {
56602
56907
  }
56603
56908
  return gIndex === globSegments.length && pIndex === pathSegments.length;
56604
56909
  }
56605
- function matchesGlobSegment(path40, glob) {
56606
- const normalizedPath = path40.replace(/\\/g, "/");
56910
+ function matchesGlobSegment(path41, glob) {
56911
+ const normalizedPath = path41.replace(/\\/g, "/");
56607
56912
  const normalizedGlob = glob.replace(/\\/g, "/");
56608
56913
  if (normalizedPath.includes("//")) {
56609
56914
  return false;
@@ -56634,8 +56939,8 @@ function simpleGlobToRegex2(glob) {
56634
56939
  function hasGlobstar(glob) {
56635
56940
  return glob.includes("**");
56636
56941
  }
56637
- function globMatches(path40, glob) {
56638
- const normalizedPath = path40.replace(/\\/g, "/");
56942
+ function globMatches(path41, glob) {
56943
+ const normalizedPath = path41.replace(/\\/g, "/");
56639
56944
  if (!glob || glob === "") {
56640
56945
  if (normalizedPath.includes("//")) {
56641
56946
  return false;
@@ -56671,7 +56976,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
56671
56976
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
56672
56977
  let testLines = 0;
56673
56978
  let codeLines = 0;
56674
- const srcDir = path39.join(workingDir, "src");
56979
+ const srcDir = path40.join(workingDir, "src");
56675
56980
  if (fs27.existsSync(srcDir)) {
56676
56981
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
56677
56982
  codeLines += lines;
@@ -56679,14 +56984,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
56679
56984
  }
56680
56985
  const possibleSrcDirs = ["lib", "app", "source", "core"];
56681
56986
  for (const dir of possibleSrcDirs) {
56682
- const dirPath = path39.join(workingDir, dir);
56987
+ const dirPath = path40.join(workingDir, dir);
56683
56988
  if (fs27.existsSync(dirPath)) {
56684
56989
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
56685
56990
  codeLines += lines;
56686
56991
  });
56687
56992
  }
56688
56993
  }
56689
- const testsDir = path39.join(workingDir, "tests");
56994
+ const testsDir = path40.join(workingDir, "tests");
56690
56995
  if (fs27.existsSync(testsDir)) {
56691
56996
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
56692
56997
  testLines += lines;
@@ -56694,7 +56999,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
56694
56999
  }
56695
57000
  const possibleTestDirs = ["test", "__tests__", "specs"];
56696
57001
  for (const dir of possibleTestDirs) {
56697
- const dirPath = path39.join(workingDir, dir);
57002
+ const dirPath = path40.join(workingDir, dir);
56698
57003
  if (fs27.existsSync(dirPath) && dirPath !== testsDir) {
56699
57004
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
56700
57005
  testLines += lines;
@@ -56709,7 +57014,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
56709
57014
  try {
56710
57015
  const entries = fs27.readdirSync(dirPath, { withFileTypes: true });
56711
57016
  for (const entry of entries) {
56712
- const fullPath = path39.join(dirPath, entry.name);
57017
+ const fullPath = path40.join(dirPath, entry.name);
56713
57018
  if (entry.isDirectory()) {
56714
57019
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
56715
57020
  continue;
@@ -56717,7 +57022,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
56717
57022
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
56718
57023
  } else if (entry.isFile()) {
56719
57024
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
56720
- const ext = path39.extname(entry.name).toLowerCase();
57025
+ const ext = path40.extname(entry.name).toLowerCase();
56721
57026
  const validExts = [
56722
57027
  ".ts",
56723
57028
  ".tsx",
@@ -56968,7 +57273,7 @@ init_dist();
56968
57273
  init_manager();
56969
57274
  init_detector();
56970
57275
  import * as fs28 from "fs";
56971
- import * as path40 from "path";
57276
+ import * as path41 from "path";
56972
57277
  import { extname as extname9 } from "path";
56973
57278
 
56974
57279
  // src/sast/rules/c.ts
@@ -57931,7 +58236,7 @@ async function sastScan(input, directory, config3) {
57931
58236
  _filesSkipped++;
57932
58237
  continue;
57933
58238
  }
57934
- const resolvedPath = path40.isAbsolute(filePath) ? filePath : path40.resolve(directory, filePath);
58239
+ const resolvedPath = path41.isAbsolute(filePath) ? filePath : path41.resolve(directory, filePath);
57935
58240
  if (!fs28.existsSync(resolvedPath)) {
57936
58241
  _filesSkipped++;
57937
58242
  continue;
@@ -58130,18 +58435,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
58130
58435
  let resolved;
58131
58436
  const isWinAbs = isWindowsAbsolutePath(inputPath);
58132
58437
  if (isWinAbs) {
58133
- resolved = path41.win32.resolve(inputPath);
58134
- } else if (path41.isAbsolute(inputPath)) {
58135
- resolved = path41.resolve(inputPath);
58438
+ resolved = path42.win32.resolve(inputPath);
58439
+ } else if (path42.isAbsolute(inputPath)) {
58440
+ resolved = path42.resolve(inputPath);
58136
58441
  } else {
58137
- resolved = path41.resolve(baseDir, inputPath);
58442
+ resolved = path42.resolve(baseDir, inputPath);
58138
58443
  }
58139
- const workspaceResolved = path41.resolve(workspaceDir);
58444
+ const workspaceResolved = path42.resolve(workspaceDir);
58140
58445
  let relative5;
58141
58446
  if (isWinAbs) {
58142
- relative5 = path41.win32.relative(workspaceResolved, resolved);
58447
+ relative5 = path42.win32.relative(workspaceResolved, resolved);
58143
58448
  } else {
58144
- relative5 = path41.relative(workspaceResolved, resolved);
58449
+ relative5 = path42.relative(workspaceResolved, resolved);
58145
58450
  }
58146
58451
  if (relative5.startsWith("..")) {
58147
58452
  return "path traversal detected";
@@ -58202,13 +58507,13 @@ async function runLintWrapped(files, directory, _config) {
58202
58507
  }
58203
58508
  async function runLintOnFiles(linter, files, workspaceDir) {
58204
58509
  const isWindows = process.platform === "win32";
58205
- const binDir = path41.join(workspaceDir, "node_modules", ".bin");
58510
+ const binDir = path42.join(workspaceDir, "node_modules", ".bin");
58206
58511
  const validatedFiles = [];
58207
58512
  for (const file3 of files) {
58208
58513
  if (typeof file3 !== "string") {
58209
58514
  continue;
58210
58515
  }
58211
- const resolvedPath = path41.resolve(file3);
58516
+ const resolvedPath = path42.resolve(file3);
58212
58517
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
58213
58518
  if (validationError) {
58214
58519
  continue;
@@ -58226,10 +58531,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
58226
58531
  }
58227
58532
  let command;
58228
58533
  if (linter === "biome") {
58229
- const biomeBin = isWindows ? path41.join(binDir, "biome.EXE") : path41.join(binDir, "biome");
58534
+ const biomeBin = isWindows ? path42.join(binDir, "biome.EXE") : path42.join(binDir, "biome");
58230
58535
  command = [biomeBin, "check", ...validatedFiles];
58231
58536
  } else {
58232
- const eslintBin = isWindows ? path41.join(binDir, "eslint.cmd") : path41.join(binDir, "eslint");
58537
+ const eslintBin = isWindows ? path42.join(binDir, "eslint.cmd") : path42.join(binDir, "eslint");
58233
58538
  command = [eslintBin, ...validatedFiles];
58234
58539
  }
58235
58540
  try {
@@ -58366,7 +58671,7 @@ async function runSecretscanWithFiles(files, directory) {
58366
58671
  skippedFiles++;
58367
58672
  continue;
58368
58673
  }
58369
- const resolvedPath = path41.resolve(file3);
58674
+ const resolvedPath = path42.resolve(file3);
58370
58675
  const validationError = validatePath(resolvedPath, directory, directory);
58371
58676
  if (validationError) {
58372
58677
  skippedFiles++;
@@ -58384,7 +58689,7 @@ async function runSecretscanWithFiles(files, directory) {
58384
58689
  };
58385
58690
  }
58386
58691
  for (const file3 of validatedFiles) {
58387
- const ext = path41.extname(file3).toLowerCase();
58692
+ const ext = path42.extname(file3).toLowerCase();
58388
58693
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
58389
58694
  skippedFiles++;
58390
58695
  continue;
@@ -58543,7 +58848,7 @@ async function runPreCheckBatch(input, workspaceDir) {
58543
58848
  warn(`pre_check_batch: Invalid file path: ${file3}`);
58544
58849
  continue;
58545
58850
  }
58546
- changedFiles.push(path41.resolve(directory, file3));
58851
+ changedFiles.push(path42.resolve(directory, file3));
58547
58852
  }
58548
58853
  if (changedFiles.length === 0) {
58549
58854
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -58694,7 +58999,7 @@ var pre_check_batch = createSwarmTool({
58694
58999
  };
58695
59000
  return JSON.stringify(errorResult, null, 2);
58696
59001
  }
58697
- const resolvedDirectory = path41.resolve(typedArgs.directory);
59002
+ const resolvedDirectory = path42.resolve(typedArgs.directory);
58698
59003
  const workspaceAnchor = resolvedDirectory;
58699
59004
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
58700
59005
  if (dirError) {
@@ -58802,7 +59107,7 @@ init_tool();
58802
59107
  init_manager2();
58803
59108
  init_create_tool();
58804
59109
  import * as fs30 from "fs";
58805
- import * as path42 from "path";
59110
+ import * as path43 from "path";
58806
59111
  function detectPlaceholderContent(args2) {
58807
59112
  const issues = [];
58808
59113
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -58906,7 +59211,7 @@ async function executeSavePlan(args2, fallbackDir) {
58906
59211
  try {
58907
59212
  await savePlan(dir, plan);
58908
59213
  try {
58909
- const markerPath = path42.join(dir, ".swarm", ".plan-write-marker");
59214
+ const markerPath = path43.join(dir, ".swarm", ".plan-write-marker");
58910
59215
  const marker = JSON.stringify({
58911
59216
  source: "save_plan",
58912
59217
  timestamp: new Date().toISOString(),
@@ -58918,7 +59223,7 @@ async function executeSavePlan(args2, fallbackDir) {
58918
59223
  return {
58919
59224
  success: true,
58920
59225
  message: "Plan saved successfully",
58921
- plan_path: path42.join(dir, ".swarm", "plan.json"),
59226
+ plan_path: path43.join(dir, ".swarm", "plan.json"),
58922
59227
  phases_count: plan.phases.length,
58923
59228
  tasks_count: tasksCount
58924
59229
  };
@@ -58957,7 +59262,7 @@ var save_plan = createSwarmTool({
58957
59262
  init_dist();
58958
59263
  init_manager();
58959
59264
  import * as fs31 from "fs";
58960
- import * as path43 from "path";
59265
+ import * as path44 from "path";
58961
59266
 
58962
59267
  // src/sbom/detectors/index.ts
58963
59268
  init_utils();
@@ -59805,7 +60110,7 @@ function findManifestFiles(rootDir) {
59805
60110
  try {
59806
60111
  const entries = fs31.readdirSync(dir, { withFileTypes: true });
59807
60112
  for (const entry of entries) {
59808
- const fullPath = path43.join(dir, entry.name);
60113
+ const fullPath = path44.join(dir, entry.name);
59809
60114
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
59810
60115
  continue;
59811
60116
  }
@@ -59814,7 +60119,7 @@ function findManifestFiles(rootDir) {
59814
60119
  } else if (entry.isFile()) {
59815
60120
  for (const pattern of patterns) {
59816
60121
  if (simpleGlobToRegex(pattern).test(entry.name)) {
59817
- manifestFiles.push(path43.relative(rootDir, fullPath));
60122
+ manifestFiles.push(path44.relative(rootDir, fullPath));
59818
60123
  break;
59819
60124
  }
59820
60125
  }
@@ -59832,11 +60137,11 @@ function findManifestFilesInDirs(directories, workingDir) {
59832
60137
  try {
59833
60138
  const entries = fs31.readdirSync(dir, { withFileTypes: true });
59834
60139
  for (const entry of entries) {
59835
- const fullPath = path43.join(dir, entry.name);
60140
+ const fullPath = path44.join(dir, entry.name);
59836
60141
  if (entry.isFile()) {
59837
60142
  for (const pattern of patterns) {
59838
60143
  if (simpleGlobToRegex(pattern).test(entry.name)) {
59839
- found.push(path43.relative(workingDir, fullPath));
60144
+ found.push(path44.relative(workingDir, fullPath));
59840
60145
  break;
59841
60146
  }
59842
60147
  }
@@ -59849,11 +60154,11 @@ function findManifestFilesInDirs(directories, workingDir) {
59849
60154
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
59850
60155
  const dirs = new Set;
59851
60156
  for (const file3 of changedFiles) {
59852
- let currentDir = path43.dirname(file3);
60157
+ let currentDir = path44.dirname(file3);
59853
60158
  while (true) {
59854
- if (currentDir && currentDir !== "." && currentDir !== path43.sep) {
59855
- dirs.add(path43.join(workingDir, currentDir));
59856
- const parent = path43.dirname(currentDir);
60159
+ if (currentDir && currentDir !== "." && currentDir !== path44.sep) {
60160
+ dirs.add(path44.join(workingDir, currentDir));
60161
+ const parent = path44.dirname(currentDir);
59857
60162
  if (parent === currentDir)
59858
60163
  break;
59859
60164
  currentDir = parent;
@@ -59937,7 +60242,7 @@ var sbom_generate = createSwarmTool({
59937
60242
  const changedFiles = obj.changed_files;
59938
60243
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
59939
60244
  const workingDir = directory;
59940
- const outputDir = path43.isAbsolute(relativeOutputDir) ? relativeOutputDir : path43.join(workingDir, relativeOutputDir);
60245
+ const outputDir = path44.isAbsolute(relativeOutputDir) ? relativeOutputDir : path44.join(workingDir, relativeOutputDir);
59941
60246
  let manifestFiles = [];
59942
60247
  if (scope === "all") {
59943
60248
  manifestFiles = findManifestFiles(workingDir);
@@ -59960,7 +60265,7 @@ var sbom_generate = createSwarmTool({
59960
60265
  const processedFiles = [];
59961
60266
  for (const manifestFile of manifestFiles) {
59962
60267
  try {
59963
- const fullPath = path43.isAbsolute(manifestFile) ? manifestFile : path43.join(workingDir, manifestFile);
60268
+ const fullPath = path44.isAbsolute(manifestFile) ? manifestFile : path44.join(workingDir, manifestFile);
59964
60269
  if (!fs31.existsSync(fullPath)) {
59965
60270
  continue;
59966
60271
  }
@@ -59977,7 +60282,7 @@ var sbom_generate = createSwarmTool({
59977
60282
  const bom = generateCycloneDX(allComponents);
59978
60283
  const bomJson = serializeCycloneDX(bom);
59979
60284
  const filename = generateSbomFilename();
59980
- const outputPath = path43.join(outputDir, filename);
60285
+ const outputPath = path44.join(outputDir, filename);
59981
60286
  fs31.writeFileSync(outputPath, bomJson, "utf-8");
59982
60287
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
59983
60288
  try {
@@ -60021,7 +60326,7 @@ var sbom_generate = createSwarmTool({
60021
60326
  init_dist();
60022
60327
  init_create_tool();
60023
60328
  import * as fs32 from "fs";
60024
- import * as path44 from "path";
60329
+ import * as path45 from "path";
60025
60330
  var SPEC_CANDIDATES = [
60026
60331
  "openapi.json",
60027
60332
  "openapi.yaml",
@@ -60053,12 +60358,12 @@ function normalizePath2(p) {
60053
60358
  }
60054
60359
  function discoverSpecFile(cwd, specFileArg) {
60055
60360
  if (specFileArg) {
60056
- const resolvedPath = path44.resolve(cwd, specFileArg);
60057
- const normalizedCwd = cwd.endsWith(path44.sep) ? cwd : cwd + path44.sep;
60361
+ const resolvedPath = path45.resolve(cwd, specFileArg);
60362
+ const normalizedCwd = cwd.endsWith(path45.sep) ? cwd : cwd + path45.sep;
60058
60363
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
60059
60364
  throw new Error("Invalid spec_file: path traversal detected");
60060
60365
  }
60061
- const ext = path44.extname(resolvedPath).toLowerCase();
60366
+ const ext = path45.extname(resolvedPath).toLowerCase();
60062
60367
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
60063
60368
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
60064
60369
  }
@@ -60072,7 +60377,7 @@ function discoverSpecFile(cwd, specFileArg) {
60072
60377
  return resolvedPath;
60073
60378
  }
60074
60379
  for (const candidate of SPEC_CANDIDATES) {
60075
- const candidatePath = path44.resolve(cwd, candidate);
60380
+ const candidatePath = path45.resolve(cwd, candidate);
60076
60381
  if (fs32.existsSync(candidatePath)) {
60077
60382
  const stats = fs32.statSync(candidatePath);
60078
60383
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -60084,7 +60389,7 @@ function discoverSpecFile(cwd, specFileArg) {
60084
60389
  }
60085
60390
  function parseSpec(specFile) {
60086
60391
  const content = fs32.readFileSync(specFile, "utf-8");
60087
- const ext = path44.extname(specFile).toLowerCase();
60392
+ const ext = path45.extname(specFile).toLowerCase();
60088
60393
  if (ext === ".json") {
60089
60394
  return parseJsonSpec(content);
60090
60395
  }
@@ -60160,7 +60465,7 @@ function extractRoutes(cwd) {
60160
60465
  return;
60161
60466
  }
60162
60467
  for (const entry of entries) {
60163
- const fullPath = path44.join(dir, entry.name);
60468
+ const fullPath = path45.join(dir, entry.name);
60164
60469
  if (entry.isSymbolicLink()) {
60165
60470
  continue;
60166
60471
  }
@@ -60170,7 +60475,7 @@ function extractRoutes(cwd) {
60170
60475
  }
60171
60476
  walkDir(fullPath);
60172
60477
  } else if (entry.isFile()) {
60173
- const ext = path44.extname(entry.name).toLowerCase();
60478
+ const ext = path45.extname(entry.name).toLowerCase();
60174
60479
  const baseName = entry.name.toLowerCase();
60175
60480
  if (![".ts", ".js", ".mjs"].includes(ext)) {
60176
60481
  continue;
@@ -60340,7 +60645,7 @@ init_secretscan();
60340
60645
  init_tool();
60341
60646
  init_create_tool();
60342
60647
  import * as fs33 from "fs";
60343
- import * as path45 from "path";
60648
+ import * as path46 from "path";
60344
60649
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
60345
60650
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
60346
60651
  function containsControlCharacters(str) {
@@ -60369,11 +60674,11 @@ function containsWindowsAttacks(str) {
60369
60674
  }
60370
60675
  function isPathInWorkspace(filePath, workspace) {
60371
60676
  try {
60372
- const resolvedPath = path45.resolve(workspace, filePath);
60677
+ const resolvedPath = path46.resolve(workspace, filePath);
60373
60678
  const realWorkspace = fs33.realpathSync(workspace);
60374
60679
  const realResolvedPath = fs33.realpathSync(resolvedPath);
60375
- const relativePath = path45.relative(realWorkspace, realResolvedPath);
60376
- if (relativePath.startsWith("..") || path45.isAbsolute(relativePath)) {
60680
+ const relativePath = path46.relative(realWorkspace, realResolvedPath);
60681
+ if (relativePath.startsWith("..") || path46.isAbsolute(relativePath)) {
60377
60682
  return false;
60378
60683
  }
60379
60684
  return true;
@@ -60385,7 +60690,7 @@ function validatePathForRead(filePath, workspace) {
60385
60690
  return isPathInWorkspace(filePath, workspace);
60386
60691
  }
60387
60692
  function extractTSSymbols(filePath, cwd) {
60388
- const fullPath = path45.join(cwd, filePath);
60693
+ const fullPath = path46.join(cwd, filePath);
60389
60694
  if (!validatePathForRead(fullPath, cwd)) {
60390
60695
  return [];
60391
60696
  }
@@ -60537,7 +60842,7 @@ function extractTSSymbols(filePath, cwd) {
60537
60842
  });
60538
60843
  }
60539
60844
  function extractPythonSymbols(filePath, cwd) {
60540
- const fullPath = path45.join(cwd, filePath);
60845
+ const fullPath = path46.join(cwd, filePath);
60541
60846
  if (!validatePathForRead(fullPath, cwd)) {
60542
60847
  return [];
60543
60848
  }
@@ -60620,7 +60925,7 @@ var symbols = createSwarmTool({
60620
60925
  }, null, 2);
60621
60926
  }
60622
60927
  const cwd = directory;
60623
- const ext = path45.extname(file3);
60928
+ const ext = path46.extname(file3);
60624
60929
  if (containsControlCharacters(file3)) {
60625
60930
  return JSON.stringify({
60626
60931
  file: file3,
@@ -60692,7 +60997,7 @@ init_dist();
60692
60997
  init_utils();
60693
60998
  init_create_tool();
60694
60999
  import * as fs34 from "fs";
60695
- import * as path46 from "path";
61000
+ import * as path47 from "path";
60696
61001
  var MAX_TEXT_LENGTH = 200;
60697
61002
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
60698
61003
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -60763,9 +61068,9 @@ function validatePathsInput(paths, cwd) {
60763
61068
  return { error: "paths contains path traversal", resolvedPath: null };
60764
61069
  }
60765
61070
  try {
60766
- const resolvedPath = path46.resolve(paths);
60767
- const normalizedCwd = path46.resolve(cwd);
60768
- const normalizedResolved = path46.resolve(resolvedPath);
61071
+ const resolvedPath = path47.resolve(paths);
61072
+ const normalizedCwd = path47.resolve(cwd);
61073
+ const normalizedResolved = path47.resolve(resolvedPath);
60769
61074
  if (!normalizedResolved.startsWith(normalizedCwd)) {
60770
61075
  return {
60771
61076
  error: "paths must be within the current working directory",
@@ -60781,7 +61086,7 @@ function validatePathsInput(paths, cwd) {
60781
61086
  }
60782
61087
  }
60783
61088
  function isSupportedExtension(filePath) {
60784
- const ext = path46.extname(filePath).toLowerCase();
61089
+ const ext = path47.extname(filePath).toLowerCase();
60785
61090
  return SUPPORTED_EXTENSIONS2.has(ext);
60786
61091
  }
60787
61092
  function findSourceFiles3(dir, files = []) {
@@ -60796,7 +61101,7 @@ function findSourceFiles3(dir, files = []) {
60796
61101
  if (SKIP_DIRECTORIES3.has(entry)) {
60797
61102
  continue;
60798
61103
  }
60799
- const fullPath = path46.join(dir, entry);
61104
+ const fullPath = path47.join(dir, entry);
60800
61105
  let stat2;
60801
61106
  try {
60802
61107
  stat2 = fs34.statSync(fullPath);
@@ -60908,7 +61213,7 @@ var todo_extract = createSwarmTool({
60908
61213
  filesToScan.push(scanPath);
60909
61214
  } else {
60910
61215
  const errorResult = {
60911
- error: `unsupported file extension: ${path46.extname(scanPath)}`,
61216
+ error: `unsupported file extension: ${path47.extname(scanPath)}`,
60912
61217
  total: 0,
60913
61218
  byPriority: { high: 0, medium: 0, low: 0 },
60914
61219
  entries: []
@@ -60955,7 +61260,7 @@ init_tool();
60955
61260
  init_schema();
60956
61261
  init_manager2();
60957
61262
  import * as fs35 from "fs";
60958
- import * as path47 from "path";
61263
+ import * as path48 from "path";
60959
61264
  init_create_tool();
60960
61265
  var VALID_STATUSES2 = [
60961
61266
  "pending",
@@ -60978,6 +61283,23 @@ function validateTaskId(taskId) {
60978
61283
  }
60979
61284
  function checkReviewerGate(taskId, workingDirectory) {
60980
61285
  try {
61286
+ const resolvedDir = workingDirectory ?? process.cwd();
61287
+ try {
61288
+ const evidencePath = path48.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
61289
+ const raw = fs35.readFileSync(evidencePath, "utf-8");
61290
+ const evidence = JSON.parse(raw);
61291
+ if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
61292
+ const allGatesMet = evidence.required_gates.every((gate) => evidence.gates[gate] != null);
61293
+ if (allGatesMet) {
61294
+ return { blocked: false, reason: "" };
61295
+ }
61296
+ const missingGates = evidence.required_gates.filter((gate) => evidence.gates[gate] == null);
61297
+ return {
61298
+ blocked: true,
61299
+ reason: `Task ${taskId} is missing required gates: [${missingGates.join(", ")}]. ` + `Required: [${evidence.required_gates.join(", ")}]. ` + `Completed: [${Object.keys(evidence.gates).join(", ")}]. ` + `Delegate the missing gate agents before marking task as completed.`
61300
+ };
61301
+ }
61302
+ } catch {}
60981
61303
  if (swarmState.agentSessions.size === 0) {
60982
61304
  return { blocked: false, reason: "" };
60983
61305
  }
@@ -61003,8 +61325,8 @@ function checkReviewerGate(taskId, workingDirectory) {
61003
61325
  stateEntries.push(`${sessionId}: ${state}`);
61004
61326
  }
61005
61327
  try {
61006
- const resolvedDir = workingDirectory ?? process.cwd();
61007
- const planPath = path47.join(resolvedDir, ".swarm", "plan.json");
61328
+ const resolvedDir2 = workingDirectory ?? process.cwd();
61329
+ const planPath = path48.join(resolvedDir2, ".swarm", "plan.json");
61008
61330
  const planRaw = fs35.readFileSync(planPath, "utf-8");
61009
61331
  const plan = JSON.parse(planRaw);
61010
61332
  for (const planPhase of plan.phases ?? []) {
@@ -61027,13 +61349,16 @@ function checkReviewerGate(taskId, workingDirectory) {
61027
61349
  function recoverTaskStateFromDelegations(taskId) {
61028
61350
  let hasReviewer = false;
61029
61351
  let hasTestEngineer = false;
61030
- for (const [, chain] of swarmState.delegationChains) {
61031
- for (const delegation of chain) {
61032
- const target = stripKnownSwarmPrefix(delegation.to);
61033
- if (target === "reviewer")
61034
- hasReviewer = true;
61035
- if (target === "test_engineer")
61036
- hasTestEngineer = true;
61352
+ for (const [sessionId, chain] of swarmState.delegationChains) {
61353
+ const session = swarmState.agentSessions.get(sessionId);
61354
+ if (session && (session.currentTaskId === taskId || session.lastCoderDelegationTaskId === taskId)) {
61355
+ for (const delegation of chain) {
61356
+ const target = stripKnownSwarmPrefix(delegation.to);
61357
+ if (target === "reviewer")
61358
+ hasReviewer = true;
61359
+ if (target === "test_engineer")
61360
+ hasTestEngineer = true;
61361
+ }
61037
61362
  }
61038
61363
  }
61039
61364
  if (!hasReviewer && !hasTestEngineer)
@@ -61112,8 +61437,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
61112
61437
  };
61113
61438
  }
61114
61439
  }
61115
- normalizedDir = path47.normalize(args2.working_directory);
61116
- const pathParts = normalizedDir.split(path47.sep);
61440
+ normalizedDir = path48.normalize(args2.working_directory);
61441
+ const pathParts = normalizedDir.split(path48.sep);
61117
61442
  if (pathParts.includes("..")) {
61118
61443
  return {
61119
61444
  success: false,
@@ -61123,10 +61448,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
61123
61448
  ]
61124
61449
  };
61125
61450
  }
61126
- const resolvedDir = path47.resolve(normalizedDir);
61451
+ const resolvedDir = path48.resolve(normalizedDir);
61127
61452
  try {
61128
61453
  const realPath = fs35.realpathSync(resolvedDir);
61129
- const planPath = path47.join(realPath, ".swarm", "plan.json");
61454
+ const planPath = path48.join(realPath, ".swarm", "plan.json");
61130
61455
  if (!fs35.existsSync(planPath)) {
61131
61456
  return {
61132
61457
  success: false,
@@ -61284,7 +61609,7 @@ var OpenCodeSwarm = async (ctx) => {
61284
61609
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
61285
61610
  preflightTriggerManager = new PTM(automationConfig);
61286
61611
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
61287
- const swarmDir = path48.resolve(ctx.directory, ".swarm");
61612
+ const swarmDir = path49.resolve(ctx.directory, ".swarm");
61288
61613
  statusArtifact = new ASA(swarmDir);
61289
61614
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
61290
61615
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {