opencode-swarm 6.82.0 → 6.82.2

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.
@@ -15,8 +15,8 @@ export interface EvidenceSummaryIntegrationConfig {
15
15
  automationConfig: AutomationConfig;
16
16
  /** Directory to run evidence analysis in */
17
17
  directory: string;
18
- /** Swarm directory for persisting summary artifacts */
19
- swarmDir: string;
18
+ /** Project root directory for persisting summary artifacts under .swarm/ */
19
+ projectDir: string;
20
20
  /** Filename for the summary artifact (default: evidence-summary.json) */
21
21
  summaryFilename?: string;
22
22
  }
package/dist/cli/index.js CHANGED
@@ -41139,15 +41139,48 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
41139
41139
  };
41140
41140
  }
41141
41141
  const resolvedDir = path26.resolve(normalizedDir);
41142
+ let statResult;
41142
41143
  try {
41143
- const realPath = fs16.realpathSync(resolvedDir);
41144
- return { success: true, directory: realPath };
41144
+ statResult = fs16.statSync(resolvedDir);
41145
41145
  } catch {
41146
41146
  return {
41147
41147
  success: false,
41148
41148
  message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
41149
41149
  };
41150
41150
  }
41151
+ if (!statResult.isDirectory()) {
41152
+ return {
41153
+ success: false,
41154
+ message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
41155
+ };
41156
+ }
41157
+ const resolvedFallback = path26.resolve(fallbackDirectory);
41158
+ let fallbackExists = false;
41159
+ try {
41160
+ fs16.statSync(resolvedFallback);
41161
+ fallbackExists = true;
41162
+ } catch {
41163
+ fallbackExists = false;
41164
+ }
41165
+ if (workingDirectory != null && workingDirectory !== "") {
41166
+ if (fallbackExists) {
41167
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path26.sep);
41168
+ if (isSubdirectory) {
41169
+ return {
41170
+ success: false,
41171
+ message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
41172
+ };
41173
+ }
41174
+ }
41175
+ return { success: true, directory: resolvedDir };
41176
+ }
41177
+ if (resolvedDir !== resolvedFallback) {
41178
+ return {
41179
+ success: false,
41180
+ message: `Invalid working_directory: path resolves to "${resolvedDir}" but fallbackDirectory ` + `"${resolvedFallback}" is not the project root. ` + `This may indicate CWD mismatch. Pass the project root path explicitly.`
41181
+ };
41182
+ }
41183
+ return { success: true, directory: resolvedDir };
41151
41184
  }
41152
41185
 
41153
41186
  // src/tools/test-runner.ts
package/dist/index.js CHANGED
@@ -23083,7 +23083,13 @@ function createGuardrailsHooks(directory, directoryOrConfig, config2, authorityC
23083
23083
  } else {
23084
23084
  guardrailsConfig = config2;
23085
23085
  }
23086
- const effectiveDirectory = typeof directory === "string" ? directory : process.cwd();
23086
+ const effectiveDirectory = (() => {
23087
+ if (typeof directory === "string")
23088
+ return directory;
23089
+ const cwd = process.cwd();
23090
+ console.warn(`[guardrails] effectiveDirectory resolved to process.cwd() "${cwd}" \u2014 ` + "pass an explicit directory string to createGuardrailsHooks to avoid .swarm artifacts in wrong locations");
23091
+ return cwd;
23092
+ })();
23087
23093
  if (guardrailsConfig?.enabled === false) {
23088
23094
  return {
23089
23095
  toolBefore: async () => {},
@@ -23545,7 +23551,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config2, authorityC
23545
23551
  const planMdPath = path9.resolve(effectiveDirectory, ".swarm", "plan.md").toLowerCase();
23546
23552
  const planJsonPath = path9.resolve(effectiveDirectory, ".swarm", "plan.json").toLowerCase();
23547
23553
  if (resolvedTarget === planMdPath || resolvedTarget === planJsonPath) {
23548
- throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
23554
+ throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use save_plan for ALL structural plan changes (adding/removing tasks, updating descriptions, dependencies, or phase names). " + "Use update_task_status() for task status only. " + "Use phase_complete() for phase transitions only.");
23549
23555
  }
23550
23556
  }
23551
23557
  if (!targetPath && (tool === "apply_patch" || tool === "patch")) {
@@ -23554,7 +23560,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config2, authorityC
23554
23560
  const planMdPath = path9.resolve(effectiveDirectory, ".swarm", "plan.md").toLowerCase();
23555
23561
  const planJsonPath = path9.resolve(effectiveDirectory, ".swarm", "plan.json").toLowerCase();
23556
23562
  if (resolvedP.toLowerCase() === planMdPath || resolvedP.toLowerCase() === planJsonPath) {
23557
- throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
23563
+ throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use save_plan for ALL structural plan changes (adding/removing tasks, updating descriptions, dependencies, or phase names). " + "Use update_task_status() for task status only. " + "Use phase_complete() for phase transitions only.");
23558
23564
  }
23559
23565
  if (isOutsideSwarmDir(p, effectiveDirectory) && (isSourceCodePath(p) || hasTraversalSegments(p))) {
23560
23566
  const session = swarmState.agentSessions.get(sessionID);
@@ -49822,15 +49828,48 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
49822
49828
  };
49823
49829
  }
49824
49830
  const resolvedDir = path34.resolve(normalizedDir);
49831
+ let statResult;
49825
49832
  try {
49826
- const realPath = fs23.realpathSync(resolvedDir);
49827
- return { success: true, directory: realPath };
49833
+ statResult = fs23.statSync(resolvedDir);
49828
49834
  } catch {
49829
49835
  return {
49830
49836
  success: false,
49831
49837
  message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
49832
49838
  };
49833
49839
  }
49840
+ if (!statResult.isDirectory()) {
49841
+ return {
49842
+ success: false,
49843
+ message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
49844
+ };
49845
+ }
49846
+ const resolvedFallback = path34.resolve(fallbackDirectory);
49847
+ let fallbackExists = false;
49848
+ try {
49849
+ fs23.statSync(resolvedFallback);
49850
+ fallbackExists = true;
49851
+ } catch {
49852
+ fallbackExists = false;
49853
+ }
49854
+ if (workingDirectory != null && workingDirectory !== "") {
49855
+ if (fallbackExists) {
49856
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path34.sep);
49857
+ if (isSubdirectory) {
49858
+ return {
49859
+ success: false,
49860
+ message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
49861
+ };
49862
+ }
49863
+ }
49864
+ return { success: true, directory: resolvedDir };
49865
+ }
49866
+ if (resolvedDir !== resolvedFallback) {
49867
+ return {
49868
+ success: false,
49869
+ message: `Invalid working_directory: path resolves to "${resolvedDir}" but fallbackDirectory ` + `"${resolvedFallback}" is not the project root. ` + `This may indicate CWD mismatch. Pass the project root path explicitly.`
49870
+ };
49871
+ }
49872
+ return { success: true, directory: resolvedDir };
49834
49873
  }
49835
49874
  var init_resolve_working_directory = () => {};
49836
49875
 
@@ -52941,7 +52980,7 @@ var init_reset_session = __esm(() => {
52941
52980
  });
52942
52981
 
52943
52982
  // src/summaries/manager.ts
52944
- import { mkdirSync as mkdirSync13, readdirSync as readdirSync10, renameSync as renameSync10, rmSync as rmSync4, statSync as statSync9 } from "fs";
52983
+ import { mkdirSync as mkdirSync13, readdirSync as readdirSync10, renameSync as renameSync10, rmSync as rmSync4, statSync as statSync10 } from "fs";
52945
52984
  import * as path39 from "path";
52946
52985
  function sanitizeSummaryId(id) {
52947
52986
  if (!id || id.length === 0) {
@@ -54521,7 +54560,7 @@ The correct tools: save_plan to create or restructure a plan (writes plan.json \
54521
54560
  .swarm/plan.md and .swarm/plan.json are READABLE but NOT DIRECTLY WRITABLE for state transitions.
54522
54561
  Task-level status changes (marking individual tasks as "completed") must use update_task_status().
54523
54562
  Phase-level completion (marking an entire phase as done) must use phase_complete().
54524
- You may write to plan.md/plan.json for STRUCTURAL changes (adding tasks, updating descriptions).
54563
+ For STRUCTURAL changes (adding tasks, updating descriptions, changing dependencies), use save_plan \u2014 do NOT write plan.md/plan.json directly.
54525
54564
  You may NOT write to plan.md/plan.json to change task completion status or phase status directly.
54526
54565
  "I'll just mark it done directly" is a bypass \u2014 equivalent to GATE_DELEGATION_BYPASS.
54527
54566
 
@@ -57437,8 +57476,8 @@ __export(exports_evidence_summary_integration, {
57437
57476
  });
57438
57477
  import { existsSync as existsSync24, mkdirSync as mkdirSync14, writeFileSync as writeFileSync6 } from "fs";
57439
57478
  import * as path43 from "path";
57440
- function persistSummary(swarmDir, artifact, filename) {
57441
- const swarmPath = path43.join(swarmDir, ".swarm");
57479
+ function persistSummary(projectDir, artifact, filename) {
57480
+ const swarmPath = path43.join(projectDir, ".swarm");
57442
57481
  if (!existsSync24(swarmPath)) {
57443
57482
  mkdirSync14(swarmPath, { recursive: true });
57444
57483
  }
@@ -57498,7 +57537,7 @@ class EvidenceSummaryIntegration {
57498
57537
  return null;
57499
57538
  }
57500
57539
  const filename = this.config.summaryFilename ?? "evidence-summary.json";
57501
- const artifactPath = persistSummary(this.config.swarmDir, artifact, filename);
57540
+ const artifactPath = persistSummary(this.config.projectDir, artifact, filename);
57502
57541
  log("[EvidenceSummaryIntegration] Summary generated and persisted", {
57503
57542
  path: artifactPath,
57504
57543
  completionRatio: artifact.overallCompletionRatio,
@@ -61565,8 +61604,8 @@ async function isGrammarAvailable(languageId) {
61565
61604
  try {
61566
61605
  const wasmFileName = getWasmFileName(normalizedId);
61567
61606
  const wasmPath = path57.join(getGrammarsDirAbsolute(), wasmFileName);
61568
- const { statSync: statSync17 } = await import("fs");
61569
- statSync17(wasmPath);
61607
+ const { statSync: statSync18 } = await import("fs");
61608
+ statSync18(wasmPath);
61570
61609
  return true;
61571
61610
  } catch {
61572
61611
  return false;
@@ -64311,7 +64350,7 @@ import * as path50 from "path";
64311
64350
  init_utils2();
64312
64351
  init_path_security();
64313
64352
  import * as fsSync2 from "fs";
64314
- import { constants as constants3, existsSync as existsSync28, realpathSync as realpathSync7 } from "fs";
64353
+ import { constants as constants3, existsSync as existsSync28, realpathSync as realpathSync6 } from "fs";
64315
64354
  import * as fsPromises3 from "fs/promises";
64316
64355
  import * as path49 from "path";
64317
64356
 
@@ -64765,13 +64804,13 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
64765
64804
  let resolved = path49.resolve(sourceDir, specifier);
64766
64805
  let realResolved;
64767
64806
  try {
64768
- realResolved = realpathSync7(resolved);
64807
+ realResolved = realpathSync6(resolved);
64769
64808
  } catch {
64770
64809
  realResolved = resolved;
64771
64810
  }
64772
64811
  let realRoot;
64773
64812
  try {
64774
- realRoot = realpathSync7(workspaceRoot);
64813
+ realRoot = realpathSync6(workspaceRoot);
64775
64814
  } catch {
64776
64815
  realRoot = path49.normalize(workspaceRoot);
64777
64816
  }
@@ -64796,7 +64835,7 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
64796
64835
  }
64797
64836
  if (found) {
64798
64837
  try {
64799
- realResolved = realpathSync7(found);
64838
+ realResolved = realpathSync6(found);
64800
64839
  } catch {
64801
64840
  realResolved = found;
64802
64841
  }
@@ -64978,14 +65017,14 @@ async function saveGraph(workspace, graph, options) {
64978
65017
  const normalizedWorkspace = path49.normalize(workspace);
64979
65018
  let realWorkspace;
64980
65019
  try {
64981
- realWorkspace = realpathSync7(workspace);
65020
+ realWorkspace = realpathSync6(workspace);
64982
65021
  } catch {
64983
65022
  realWorkspace = normalizedWorkspace;
64984
65023
  }
64985
65024
  const normalizedGraphRoot = path49.normalize(graph.workspaceRoot);
64986
65025
  let realGraphRoot;
64987
65026
  try {
64988
- realGraphRoot = realpathSync7(graph.workspaceRoot);
65027
+ realGraphRoot = realpathSync6(graph.workspaceRoot);
64989
65028
  } catch {
64990
65029
  realGraphRoot = normalizedGraphRoot;
64991
65030
  }
@@ -82030,6 +82069,30 @@ async function executeSavePlan(args2, fallbackDir) {
82030
82069
  recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
82031
82070
  };
82032
82071
  }
82072
+ if (args2.working_directory && fallbackDir) {
82073
+ const resolvedTarget = path87.resolve(args2.working_directory);
82074
+ const resolvedRoot = path87.resolve(fallbackDir);
82075
+ let fallbackExists = false;
82076
+ try {
82077
+ fs72.accessSync(resolvedRoot, fs72.constants.F_OK);
82078
+ fallbackExists = true;
82079
+ } catch {
82080
+ fallbackExists = false;
82081
+ }
82082
+ if (fallbackExists) {
82083
+ const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path87.sep);
82084
+ if (isSubdirectory) {
82085
+ return {
82086
+ success: false,
82087
+ message: `working_directory must be the project root. ` + `Got "${args2.working_directory}" (resolves to "${resolvedTarget}"), ` + `which is a subdirectory of fallback "${resolvedRoot}". ` + `Omit working_directory or pass the project root explicitly.`,
82088
+ errors: [
82089
+ `working_directory "${resolvedTarget}" is a subdirectory of fallback "${resolvedRoot}"`
82090
+ ],
82091
+ recovery_guidance: `Pass working_directory: "${resolvedRoot}" or omit the field entirely.`
82092
+ };
82093
+ }
82094
+ }
82095
+ }
82033
82096
  let specMtime;
82034
82097
  let specHash;
82035
82098
  if (process.env.SWARM_SKIP_SPEC_GATE !== "1") {
@@ -82217,7 +82280,7 @@ async function executeSavePlan(args2, fallbackDir) {
82217
82280
  }
82218
82281
  }
82219
82282
  var save_plan = createSwarmTool({
82220
- description: "Save a structured implementation plan to .swarm/plan.json and .swarm/plan.md. " + "Task descriptions and phase names MUST contain real content from the spec \u2014 " + "bracket placeholders like [task] or [Project] will be rejected.",
82283
+ description: "Save or revise a structured implementation plan to .swarm/plan.json and .swarm/plan.md. " + "Use this tool for all structural plan changes on an existing plan (adding/removing tasks, updating descriptions, dependencies, or phase names) \u2014 existing task statuses are preserved by default (set reset_statuses: true to start fresh). " + "Task descriptions and phase names MUST contain real content from the spec \u2014 " + "bracket placeholders like [task] or [Project] will be rejected.",
82221
82284
  args: {
82222
82285
  title: tool.schema.string().min(1).describe("Plan title \u2014 the REAL project name from the spec. NOT a placeholder like [Project]."),
82223
82286
  swarm_id: tool.schema.string().min(1).describe('Swarm identifier (e.g. "mega")'),
@@ -83743,7 +83806,7 @@ async function ripgrepSearch(opts) {
83743
83806
  stderr: "pipe",
83744
83807
  cwd: opts.workspace
83745
83808
  });
83746
- const timeout = new Promise((resolve37) => setTimeout(() => resolve37("timeout"), REGEX_TIMEOUT_MS));
83809
+ const timeout = new Promise((resolve38) => setTimeout(() => resolve38("timeout"), REGEX_TIMEOUT_MS));
83747
83810
  const exitPromise = proc.exited;
83748
83811
  const result = await Promise.race([exitPromise, timeout]);
83749
83812
  if (result === "timeout") {
@@ -86956,7 +87019,7 @@ var OpenCodeSwarm = async (ctx) => {
86956
87019
  createEvidenceSummaryIntegration2({
86957
87020
  automationConfig,
86958
87021
  directory: ctx.directory,
86959
- swarmDir: ctx.directory,
87022
+ projectDir: ctx.directory,
86960
87023
  summaryFilename: "evidence-summary.json"
86961
87024
  });
86962
87025
  log("Evidence summary integration initialized", {
@@ -0,0 +1 @@
1
+ export {};
@@ -20,6 +20,11 @@ export interface SavePlanArgs {
20
20
  acceptance?: string;
21
21
  }>;
22
22
  }>;
23
+ /**
24
+ * Must be the project root directory. When provided, it anchors all .swarm directory
25
+ * creation and plan file operations to the project root (issue #577).
26
+ * Omit to use the fallback directory (injected by createSwarmTool, typically process.cwd()).
27
+ */
23
28
  working_directory?: string;
24
29
  /**
25
30
  * When true, all task statuses are reset to 'pending' and existing completed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "6.82.0",
3
+ "version": "6.82.2",
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",