opencode-swarm 7.100.0 → 7.101.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.
@@ -84,6 +84,7 @@ import {
84
84
  computeSpecHash,
85
85
  derivePlanId,
86
86
  derivePlanMarkdown,
87
+ detectSpeckit,
87
88
  disableEpicMode,
88
89
  enableEpicMode,
89
90
  getDurableGateEvidenceStatusForTask,
@@ -108,13 +109,15 @@ import {
108
109
  readTaskScopes,
109
110
  resetToMainAfterMerge,
110
111
  resetToRemoteBranch,
112
+ resolveSpeckitProjection,
111
113
  sanitizeTaskId,
112
114
  saveEvidence,
113
115
  savePlan,
114
116
  transientBackoff,
115
117
  validateProjectRoot,
118
+ validateSpeckit,
116
119
  writeProjectedSpecSync
117
- } from "./index-y3x3gvy6.js";
120
+ } from "./index-bnkd9ejn.js";
118
121
  import {
119
122
  _internals as _internals2,
120
123
  _internals1 as _internals3,
@@ -909,7 +912,7 @@ var init_executor = __esm(() => {
909
912
  // package.json
910
913
  var package_default = {
911
914
  name: "opencode-swarm",
912
- version: "7.100.0",
915
+ version: "7.101.0",
913
916
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
914
917
  main: "dist/index.js",
915
918
  types: "dist/index.d.ts",
@@ -13089,6 +13092,7 @@ var ACTIVE_STATE_TO_CLEAN = [
13089
13092
  "swarm.db-shm",
13090
13093
  "swarm.db-wal"
13091
13094
  ];
13095
+ var TERMINAL_STATE_FILES = ["plan.json", "plan-ledger.jsonl"];
13092
13096
  var KNOWLEDGE_FAMILY_ARTIFACTS = new Set([
13093
13097
  "knowledge.jsonl",
13094
13098
  "knowledge-rejected.jsonl"
@@ -13579,6 +13583,10 @@ async function runCleanStage(ctx) {
13579
13583
  }
13580
13584
  if (!ctx.archivedActiveStateFiles.has(artifact)) {
13581
13585
  const reason = ctx.archiveFailureReasons?.get(artifact);
13586
+ if (TERMINAL_STATE_FILES.includes(artifact)) {
13587
+ ctx.warnings.push(reason ? `${artifact} was not archived (${reason}); removing it anyway to prevent CLOSED-plan resurrection next session \u2014 no archive copy retained.` : `${artifact} was not archived; removing it anyway to prevent CLOSED-plan resurrection next session \u2014 no archive copy retained.`);
13588
+ continue;
13589
+ }
13582
13590
  ctx.warnings.push(reason ? `Preserved ${artifact} because it was not successfully archived: ${reason}.` : `Preserved ${artifact} because it was not successfully archived.`);
13583
13591
  continue;
13584
13592
  }
@@ -13686,6 +13694,20 @@ async function runCleanStage(ctx) {
13686
13694
  if (tmpFilesRemoved > 0) {
13687
13695
  cleanedFiles.push(`${tmpFilesRemoved} .tmp.* file(s)`);
13688
13696
  }
13697
+ for (const terminalFile of TERMINAL_STATE_FILES) {
13698
+ try {
13699
+ await fs13.unlink(path26.join(ctx.swarmDir, terminalFile));
13700
+ if (!cleanedFiles.includes(terminalFile)) {
13701
+ cleanedFiles.push(terminalFile);
13702
+ }
13703
+ } catch (err) {
13704
+ const errno = err?.code;
13705
+ if (errno === "ENOENT") {} else {
13706
+ const reason = err instanceof Error ? err.message : String(err);
13707
+ ctx.warnings.push(`Failed to remove terminal-state file ${terminalFile} [${errno ?? "unknown"}]: ${reason}`);
13708
+ }
13709
+ }
13710
+ }
13689
13711
  clearAllScopes(ctx.directory);
13690
13712
  const contextPath = path26.join(ctx.swarmDir, "context.md");
13691
13713
  const contextContent = [
@@ -16153,6 +16175,15 @@ async function checkKnowledgeHealth(directory) {
16153
16175
  // src/services/diagnose-service.ts
16154
16176
  var { version: version2 } = package_default;
16155
16177
  var sandboxCapabilityProbe = new SandboxCapabilityProbe;
16178
+ var REQUIRED_CACHE_GRAMMAR_ASSETS = [
16179
+ "tree-sitter.wasm",
16180
+ "tree-sitter-javascript.wasm",
16181
+ "tree-sitter-typescript.wasm"
16182
+ ];
16183
+ function resolveCachePackageRoot(cachePath) {
16184
+ const nestedPackageRoot = path33.join(cachePath, "node_modules", "opencode-swarm");
16185
+ return existsSync18(nestedPackageRoot) ? nestedPackageRoot : cachePath;
16186
+ }
16156
16187
  var _internals24 = {
16157
16188
  detectSandboxCapability: () => sandboxCapabilityProbe.detect(),
16158
16189
  getSandboxExecutor: getExecutor
@@ -16933,14 +16964,21 @@ async function getDiagnoseData(directory) {
16933
16964
  cacheRows.push(`\u2B1C ${cachePath} \u2014 absent`);
16934
16965
  continue;
16935
16966
  }
16936
- const pkgJsonPath = path33.join(cachePath, "package.json");
16967
+ const packageRoot = resolveCachePackageRoot(cachePath);
16968
+ const cacheLabel = packageRoot === cachePath ? cachePath : `${cachePath} -> ${packageRoot}`;
16969
+ const missingGrammarAssets = REQUIRED_CACHE_GRAMMAR_ASSETS.filter((file) => !existsSync18(path33.join(packageRoot, "dist", "lang", "grammars", file)));
16970
+ const pkgJsonPath = path33.join(packageRoot, "package.json");
16937
16971
  try {
16938
16972
  const raw = readFileSync10(pkgJsonPath, "utf-8");
16939
16973
  const parsed = JSON.parse(raw);
16940
16974
  const installedVersion = typeof parsed.version === "string" ? parsed.version : "?";
16941
- cacheRows.push(`\u2705 ${cachePath} \u2014 v${installedVersion}`);
16975
+ if (missingGrammarAssets.length > 0) {
16976
+ cacheRows.push(`\u26A0\uFE0F ${cacheLabel} \u2014 v${installedVersion}, missing grammar assets: ${missingGrammarAssets.join(", ")}; run \`bunx opencode-swarm update\` and restart OpenCode`);
16977
+ continue;
16978
+ }
16979
+ cacheRows.push(`\u2705 ${cacheLabel} \u2014 v${installedVersion}`);
16942
16980
  } catch {
16943
- cacheRows.push(`\u26A0\uFE0F ${cachePath} \u2014 present (package.json unreadable)`);
16981
+ cacheRows.push(`\u26A0\uFE0F ${cacheLabel} \u2014 present (package.json unreadable)`);
16944
16982
  }
16945
16983
  } catch {
16946
16984
  cacheRows.push(`\u26A0\uFE0F ${cachePath} \u2014 status unknown (read error)`);
@@ -17967,7 +18005,7 @@ async function handleEvidenceCommand(directory, args) {
17967
18005
  return formatTaskEvidenceMarkdown(evidenceData);
17968
18006
  }
17969
18007
  async function handleEvidenceSummaryCommand(directory) {
17970
- const { buildEvidenceSummary } = await import("./evidence-summary-service-ch74hb16.js");
18008
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-c4d1c2t3.js");
17971
18009
  const artifact = await buildEvidenceSummary(directory);
17972
18010
  if (!artifact) {
17973
18011
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -31848,15 +31886,31 @@ async function handleRollbackCommand(directory, args) {
31848
31886
  }
31849
31887
 
31850
31888
  // src/commands/sdd.ts
31889
+ import * as fs31 from "fs";
31890
+ import * as path63 from "path";
31891
+ var SWARM_SPEC_REL = path63.join(".swarm", "spec.md");
31851
31892
  var USAGE9 = `Usage:
31852
- /swarm sdd status [--json]
31853
- /swarm sdd validate [--json] [--change <id>]
31854
- /swarm sdd project [--dry-run] [--json] [--change <id>]
31893
+ /swarm sdd status [--json] [--source <provider>]
31894
+ /swarm sdd validate [--json] [--change <id>] [--source <provider>] [--feature <id>]
31895
+ /swarm sdd project [--dry-run] [--json] [--change <id>] [--source <provider>] [--feature <id>]
31896
+
31897
+ Source options (required when both openspec and speckit are detected):
31898
+ --source <swarm|openspec|speckit> select the SDD provider explicitly
31899
+ --feature <id> Spec-Kit feature directory name to project
31900
+ (required when multiple Spec-Kit features exist)
31855
31901
 
31856
31902
  OpenSpec-compatible SDD support:
31857
31903
  - reads checked-in openspec/specs and openspec/changes artifacts
31858
31904
  - produces one effective Swarm spec for planning
31859
- - keeps tasks.md as proposal input; .swarm/plan-ledger.jsonl remains execution state`;
31905
+ - keeps tasks.md as proposal input; .swarm/plan-ledger.jsonl remains execution state
31906
+
31907
+ Spec-Kit SDD support:
31908
+ - detects .specify/ marker + specs/NNN-feature-name/spec.md layout
31909
+ - projects a single Spec-Kit feature into .swarm/spec.md via /swarm sdd project
31910
+ - use --source speckit or --source openspec to disambiguate when both are present`;
31911
+ function isUnsafeId(value) {
31912
+ return value.includes("/") || value.includes("\\") || value.includes("..") || value.includes("[") || value.includes("]") || containsControlChars(value);
31913
+ }
31860
31914
  function parseArgs10(args) {
31861
31915
  const parsed = { json: false, dryRun: false };
31862
31916
  for (let i = 0;i < args.length; i++) {
@@ -31870,13 +31924,37 @@ function parseArgs10(args) {
31870
31924
  return { ...parsed, error: "--change requires a value" };
31871
31925
  }
31872
31926
  const value = args[++i];
31873
- if (value.includes("/") || value.includes("\\") || value.includes("..") || value.includes("[") || value.includes("]")) {
31927
+ if (isUnsafeId(value)) {
31874
31928
  return {
31875
31929
  ...parsed,
31876
- error: "--change must be a single OpenSpec change id with no path separators, traversal, or brackets"
31930
+ error: "--change must be a single OpenSpec change id with no path separators, traversal, brackets, or control characters"
31877
31931
  };
31878
31932
  }
31879
31933
  parsed.changeId = value;
31934
+ } else if (token === "--source") {
31935
+ if (i + 1 >= args.length || args[i + 1].startsWith("--")) {
31936
+ return { ...parsed, error: "--source requires a value" };
31937
+ }
31938
+ const value = args[++i];
31939
+ if (value !== "swarm" && value !== "openspec" && value !== "speckit") {
31940
+ return {
31941
+ ...parsed,
31942
+ error: `--source must be one of: swarm | openspec | speckit; got "${value}"`
31943
+ };
31944
+ }
31945
+ parsed.source = value;
31946
+ } else if (token === "--feature") {
31947
+ if (i + 1 >= args.length || args[i + 1].startsWith("--")) {
31948
+ return { ...parsed, error: "--feature requires a value" };
31949
+ }
31950
+ const value = args[++i];
31951
+ if (isUnsafeId(value)) {
31952
+ return {
31953
+ ...parsed,
31954
+ error: "--feature must be a single Spec-Kit feature directory name with no path separators, traversal, brackets, or control characters"
31955
+ };
31956
+ }
31957
+ parsed.feature = value;
31880
31958
  } else {
31881
31959
  return { ...parsed, error: `Unknown flag "${token}"` };
31882
31960
  }
@@ -31887,16 +31965,73 @@ function formatList(items) {
31887
31965
  return items.length > 0 ? items.map((item) => `- ${item}`).join(`
31888
31966
  `) : "- none";
31889
31967
  }
31968
+ function formatSpeckitError(resolution) {
31969
+ switch (resolution.kind) {
31970
+ case "empty":
31971
+ return "Spec-Kit layout detected (.specify/ marker present) but no feature directories found under specs/.";
31972
+ case "ambiguous":
31973
+ return [
31974
+ `Multiple Spec-Kit features detected: ${resolution.features.join(", ")}.`,
31975
+ "Use --feature <id> to select one."
31976
+ ].join(`
31977
+ `);
31978
+ case "unknown_feature":
31979
+ return [
31980
+ `Unknown Spec-Kit feature '${resolution.feature}'.`,
31981
+ `Available features: ${resolution.available.join(", ")}.`
31982
+ ].join(`
31983
+ `);
31984
+ case "zero_requirements":
31985
+ return `Spec-Kit feature '${resolution.feature}' contains no parsable functional requirements.`;
31986
+ case "too_large":
31987
+ return `Spec-Kit feature '${resolution.feature}' projected output exceeds the size limit (${resolution.bytes} bytes).`;
31988
+ case "not_speckit":
31989
+ return "No Spec-Kit layout detected (.specify/ marker not present).";
31990
+ case "ok":
31991
+ return "";
31992
+ }
31993
+ }
31890
31994
  async function handleSddStatusCommand(directory, args) {
31891
31995
  const parsed = parseArgs10(args);
31892
31996
  if (parsed.error)
31893
31997
  return `Error: ${parsed.error}
31894
31998
 
31895
31999
  ${USAGE9}`;
31896
- const status = loadSddStatusSync(directory);
31897
- if (parsed.json)
31898
- return JSON.stringify(status, null, 2);
32000
+ if (parsed.feature && parsed.source && parsed.source !== "speckit") {
32001
+ return `Error: --feature is only valid with --source speckit
32002
+
32003
+ ${USAGE9}`;
32004
+ }
32005
+ const speckitDetection = detectSpeckit(directory);
32006
+ const speckitPresent = speckitDetection.features.length > 0;
32007
+ const nativeSpecExists = fs31.existsSync(path63.join(directory, SWARM_SPEC_REL));
32008
+ if (speckitPresent && !parsed.source && !nativeSpecExists) {
32009
+ const openspecProjection = buildOpenSpecProjectionSync(directory);
32010
+ if (openspecProjection !== null) {
32011
+ const errLines = [
32012
+ "Error: Multiple SDD sources detected (openspec, speckit).",
32013
+ "Pass --source openspec or --source speckit to select a provider."
32014
+ ];
32015
+ return parsed.json ? JSON.stringify({ error: errLines.join(" "), sources: ["openspec", "speckit"] }, null, 2) : errLines.join(`
32016
+ `);
32017
+ }
32018
+ }
32019
+ const status = loadSddStatusSync(directory, {
32020
+ source: parsed.source,
32021
+ feature: parsed.feature
32022
+ });
32023
+ if (parsed.json) {
32024
+ return JSON.stringify({ ...status, speckit: speckitDetection }, null, 2);
32025
+ }
31899
32026
  const changes = status.changes.map((change) => `${change.id} (${change.specs.length} spec file${change.specs.length === 1 ? "" : "s"}, proposal=${change.proposal}, design=${change.design}, tasks=${change.tasks})`);
32027
+ const speckitLines = [
32028
+ "### Spec-Kit",
32029
+ `Spec-Kit provider: ${speckitDetection.markerPresent ? "detected" : "not present"}`,
32030
+ speckitDetection.features.length > 0 ? `Features:
32031
+ ${speckitDetection.features.map((f) => `- ${f.featureId}`).join(`
32032
+ `)}` : "Features: none"
32033
+ ].join(`
32034
+ `);
31900
32035
  return [
31901
32036
  "## SDD Status",
31902
32037
  "",
@@ -31906,6 +32041,8 @@ ${USAGE9}`;
31906
32041
  `Current OpenSpec specs: ${status.currentSpecs.length}`,
31907
32042
  `Active OpenSpec changes: ${status.changes.length}`,
31908
32043
  "",
32044
+ speckitLines,
32045
+ "",
31909
32046
  "### Changes",
31910
32047
  formatList(changes),
31911
32048
  "",
@@ -31931,7 +32068,114 @@ async function handleSddValidateCommand(directory, args) {
31931
32068
  return `Error: ${parsed.error}
31932
32069
 
31933
32070
  ${USAGE9}`;
31934
- const status = loadSddStatusSync(directory);
32071
+ if (parsed.feature && parsed.source && parsed.source !== "speckit") {
32072
+ return `Error: --feature is only valid with --source speckit
32073
+
32074
+ ${USAGE9}`;
32075
+ }
32076
+ let useSpeckit = false;
32077
+ const nativeSpecExists = fs31.existsSync(path63.join(directory, SWARM_SPEC_REL));
32078
+ if (parsed.source === "speckit") {
32079
+ useSpeckit = true;
32080
+ } else if (!parsed.source && !nativeSpecExists) {
32081
+ const speckitDetection = detectSpeckit(directory);
32082
+ if (speckitDetection.features.length > 0) {
32083
+ const openspecProjection = buildOpenSpecProjectionSync(directory);
32084
+ if (openspecProjection !== null) {
32085
+ const errLines = [
32086
+ "Error: Multiple SDD sources detected (openspec, speckit).",
32087
+ "Pass --source openspec or --source speckit to select a provider."
32088
+ ];
32089
+ return parsed.json ? JSON.stringify({
32090
+ valid: false,
32091
+ error: errLines.join(" "),
32092
+ sources: ["openspec", "speckit"]
32093
+ }, null, 2) : errLines.join(`
32094
+ `);
32095
+ }
32096
+ useSpeckit = true;
32097
+ } else if (speckitDetection.markerPresent) {
32098
+ const openspecProjection = buildOpenSpecProjectionSync(directory);
32099
+ if (openspecProjection === null) {
32100
+ const resolution = resolveSpeckitProjection(directory);
32101
+ return parsed.json ? JSON.stringify({ valid: false, error: formatSpeckitError(resolution) }, null, 2) : `Error: ${formatSpeckitError(resolution)}
32102
+
32103
+ ${USAGE9}`;
32104
+ }
32105
+ }
32106
+ }
32107
+ if (useSpeckit) {
32108
+ const { resolution, problems } = validateSpeckit(directory, {
32109
+ feature: parsed.feature
32110
+ });
32111
+ if (resolution.kind !== "ok" && resolution.kind !== "zero_requirements") {
32112
+ return parsed.json ? JSON.stringify({ valid: false, error: formatSpeckitError(resolution) }, null, 2) : `Error: ${formatSpeckitError(resolution)}
32113
+
32114
+ ${USAGE9}`;
32115
+ }
32116
+ const spec = resolution.kind === "ok" ? resolution.spec : null;
32117
+ const result2 = {
32118
+ valid: problems.length === 0 && resolution.kind === "ok",
32119
+ provider: "speckit_projection",
32120
+ changeId: null,
32121
+ sourcePaths: spec?.sourcePaths ?? [],
32122
+ hash: spec?.hash ?? null,
32123
+ errors: problems,
32124
+ warnings: spec?.warnings ?? []
32125
+ };
32126
+ if (parsed.json)
32127
+ return JSON.stringify(result2, null, 2);
32128
+ return [
32129
+ `SDD validation: ${result2.valid ? "valid" : "invalid"}`,
32130
+ `Provider: ${result2.provider}`,
32131
+ `Projected sources: ${result2.sourcePaths.length}`,
32132
+ result2.errors.length > 0 ? `
32133
+ Errors:
32134
+ ${formatList(result2.errors)}` : "",
32135
+ result2.warnings.length > 0 ? `
32136
+ Warnings:
32137
+ ${formatList(result2.warnings)}` : ""
32138
+ ].join(`
32139
+ `);
32140
+ }
32141
+ if (parsed.source === "swarm") {
32142
+ const status2 = loadSddStatusSync(directory, {
32143
+ source: "swarm",
32144
+ feature: parsed.feature
32145
+ });
32146
+ const filteredErrors = status2.errors.filter((e) => !e.includes("openspec/") && !e.includes("proposal.md") && !e.includes("tasks.md") && !e.includes("specs/**/spec.md"));
32147
+ const filteredWarnings = status2.warnings.filter((w) => !w.includes("openspec/") && !w.includes("proposal.md") && !w.includes("tasks.md") && !w.includes("specs/**/spec.md"));
32148
+ const result2 = {
32149
+ valid: status2.effectiveSpec !== null && filteredErrors.length === 0,
32150
+ provider: status2.provider,
32151
+ changeId: null,
32152
+ sourcePaths: status2.effectiveSpec?.sourcePaths ?? [],
32153
+ hash: status2.effectiveSpec?.hash ?? null,
32154
+ errors: filteredErrors,
32155
+ warnings: [
32156
+ ...filteredWarnings,
32157
+ ...status2.effectiveSpec?.warnings ?? []
32158
+ ]
32159
+ };
32160
+ if (parsed.json)
32161
+ return JSON.stringify(result2, null, 2);
32162
+ return [
32163
+ `SDD validation: ${result2.valid ? "valid" : "invalid"}`,
32164
+ `Provider: ${result2.provider}`,
32165
+ `Projected sources: ${result2.sourcePaths.length}`,
32166
+ result2.errors.length > 0 ? `
32167
+ Errors:
32168
+ ${formatList(result2.errors)}` : "",
32169
+ result2.warnings.length > 0 ? `
32170
+ Warnings:
32171
+ ${formatList(result2.warnings)}` : ""
32172
+ ].join(`
32173
+ `);
32174
+ }
32175
+ const status = loadSddStatusSync(directory, {
32176
+ source: parsed.source,
32177
+ feature: parsed.feature
32178
+ });
31935
32179
  const projection = buildOpenSpecProjectionSync(directory, {
31936
32180
  changeId: parsed.changeId
31937
32181
  });
@@ -31973,6 +32217,86 @@ async function handleSddProjectCommand(directory, args) {
31973
32217
  return `Error: ${parsed.error}
31974
32218
 
31975
32219
  ${USAGE9}`;
32220
+ if (parsed.feature && parsed.source && parsed.source !== "speckit") {
32221
+ return `Error: --feature is only valid with --source speckit
32222
+
32223
+ ${USAGE9}`;
32224
+ }
32225
+ if (parsed.source === "swarm") {
32226
+ return `Error: --source swarm selects the native .swarm/spec.md and does not generate a projection. Use --source openspec or --source speckit.
32227
+
32228
+ ${USAGE9}`;
32229
+ }
32230
+ let useSpeckit = false;
32231
+ if (parsed.source === "speckit") {
32232
+ useSpeckit = true;
32233
+ } else if (!parsed.source) {
32234
+ const speckitDetection = detectSpeckit(directory);
32235
+ if (speckitDetection.features.length > 0) {
32236
+ const openspecProjection = buildOpenSpecProjectionSync(directory);
32237
+ if (openspecProjection !== null) {
32238
+ return [
32239
+ "Error: Multiple SDD sources detected (openspec, speckit).",
32240
+ "Pass --source openspec or --source speckit to select a provider."
32241
+ ].join(`
32242
+ `);
32243
+ }
32244
+ useSpeckit = true;
32245
+ } else if (speckitDetection.markerPresent) {
32246
+ const openspecProjection = buildOpenSpecProjectionSync(directory);
32247
+ if (openspecProjection === null) {
32248
+ const resolution = resolveSpeckitProjection(directory);
32249
+ return `Error: ${formatSpeckitError(resolution)}
32250
+
32251
+ ${USAGE9}`;
32252
+ }
32253
+ }
32254
+ }
32255
+ if (useSpeckit) {
32256
+ const resolution = resolveSpeckitProjection(directory, {
32257
+ feature: parsed.feature
32258
+ });
32259
+ if (resolution.kind !== "ok") {
32260
+ return `Error: ${formatSpeckitError(resolution)}
32261
+
32262
+ ${USAGE9}`;
32263
+ }
32264
+ const result2 = writeProjectedSpecSync(directory, {
32265
+ source: "speckit",
32266
+ feature: resolution.feature,
32267
+ dryRun: parsed.dryRun
32268
+ });
32269
+ const response2 = {
32270
+ written: result2.written,
32271
+ dryRun: parsed.dryRun,
32272
+ path: result2.path,
32273
+ archivePath: result2.archivePath ?? null,
32274
+ hash: result2.projection?.hash ?? null,
32275
+ sourcePaths: result2.projection?.sourcePaths ?? [],
32276
+ warnings: result2.projection?.warnings ?? []
32277
+ };
32278
+ if (parsed.json)
32279
+ return JSON.stringify(response2, null, 2);
32280
+ if (!result2.projection) {
32281
+ return [
32282
+ "SDD projection failed: no valid Spec-Kit projection could be built.",
32283
+ "",
32284
+ USAGE9
32285
+ ].join(`
32286
+ `);
32287
+ }
32288
+ return [
32289
+ parsed.dryRun ? "SDD projection preview" : "SDD projection written",
32290
+ `Path: ${result2.path}`,
32291
+ `Hash: ${result2.projection.hash}`,
32292
+ `Sources: ${result2.projection.sourcePaths.length}`,
32293
+ result2.archivePath ? `Archived previous spec: ${result2.archivePath}` : "",
32294
+ result2.projection.warnings.length > 0 ? `
32295
+ Warnings:
32296
+ ${formatList(result2.projection.warnings)}` : ""
32297
+ ].join(`
32298
+ `);
32299
+ }
31976
32300
  const result = writeProjectedSpecSync(directory, {
31977
32301
  changeId: parsed.changeId,
31978
32302
  dryRun: parsed.dryRun
@@ -32061,13 +32385,13 @@ Ensure this is a git repository with commit history.`;
32061
32385
  const report = reportLines.filter(Boolean).join(`
32062
32386
  `);
32063
32387
  try {
32064
- const fs31 = await import("fs/promises");
32065
- const path63 = await import("path");
32066
- const reportPath = path63.join(directory, ".swarm", "simulate-report.md");
32067
- await fs31.mkdir(path63.dirname(reportPath), { recursive: true });
32068
- const reportTempPath = path63.join(path63.dirname(reportPath), `${path63.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
32388
+ const fs32 = await import("fs/promises");
32389
+ const path64 = await import("path");
32390
+ const reportPath = path64.join(directory, ".swarm", "simulate-report.md");
32391
+ await fs32.mkdir(path64.dirname(reportPath), { recursive: true });
32392
+ const reportTempPath = path64.join(path64.dirname(reportPath), `${path64.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
32069
32393
  try {
32070
- await fs31.writeFile(reportTempPath, report, "utf-8");
32394
+ await fs32.writeFile(reportTempPath, report, "utf-8");
32071
32395
  renameSync11(reportTempPath, reportPath);
32072
32396
  } catch (err) {
32073
32397
  try {
@@ -32094,20 +32418,20 @@ async function handleSpecifyCommand(_directory, args) {
32094
32418
  // src/services/status-service.ts
32095
32419
  import * as fsSync3 from "fs";
32096
32420
  import { readFile as readFile16 } from "fs/promises";
32097
- import * as path64 from "path";
32421
+ import * as path65 from "path";
32098
32422
 
32099
32423
  // src/turbo/lean/state.ts
32100
32424
  init_logger();
32101
- import * as fs31 from "fs";
32102
- import * as path63 from "path";
32425
+ import * as fs32 from "fs";
32426
+ import * as path64 from "path";
32103
32427
  var STATE_FILE3 = "turbo-state.json";
32104
32428
  function nowISO3() {
32105
32429
  return new Date().toISOString();
32106
32430
  }
32107
32431
  function ensureSwarmDir2(directory) {
32108
- const swarmDir = path63.resolve(directory, ".swarm");
32109
- if (!fs31.existsSync(swarmDir)) {
32110
- fs31.mkdirSync(swarmDir, { recursive: true });
32432
+ const swarmDir = path64.resolve(directory, ".swarm");
32433
+ if (!fs32.existsSync(swarmDir)) {
32434
+ fs32.mkdirSync(swarmDir, { recursive: true });
32111
32435
  }
32112
32436
  return swarmDir;
32113
32437
  }
@@ -32150,17 +32474,17 @@ function markStateUnreadable2(directory, reason) {
32150
32474
  }
32151
32475
  function readPersisted2(directory) {
32152
32476
  try {
32153
- const filePath = path63.join(directory, ".swarm", STATE_FILE3);
32154
- if (!fs31.existsSync(filePath)) {
32477
+ const filePath = path64.join(directory, ".swarm", STATE_FILE3);
32478
+ if (!fs32.existsSync(filePath)) {
32155
32479
  const seed = emptyPersisted2();
32156
32480
  try {
32157
32481
  ensureSwarmDir2(directory);
32158
- fs31.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
32482
+ fs32.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
32159
32483
  `, "utf-8");
32160
32484
  } catch {}
32161
32485
  return seed;
32162
32486
  }
32163
- const raw = fs31.readFileSync(filePath, "utf-8");
32487
+ const raw = fs32.readFileSync(filePath, "utf-8");
32164
32488
  const parsed = JSON.parse(raw);
32165
32489
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
32166
32490
  markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
@@ -32186,7 +32510,7 @@ function writePersisted2(directory, persisted) {
32186
32510
  let payload;
32187
32511
  try {
32188
32512
  ensureSwarmDir2(directory);
32189
- filePath = path63.join(directory, ".swarm", STATE_FILE3);
32513
+ filePath = path64.join(directory, ".swarm", STATE_FILE3);
32190
32514
  tmpPath = `${filePath}.tmp.${Date.now()}`;
32191
32515
  persisted.updatedAt = nowISO3();
32192
32516
  payload = `${JSON.stringify(persisted, null, 2)}
@@ -32197,14 +32521,14 @@ function writePersisted2(directory, persisted) {
32197
32521
  throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
32198
32522
  }
32199
32523
  try {
32200
- fs31.writeFileSync(tmpPath, payload, "utf-8");
32201
- fs31.renameSync(tmpPath, filePath);
32524
+ fs32.writeFileSync(tmpPath, payload, "utf-8");
32525
+ fs32.renameSync(tmpPath, filePath);
32202
32526
  } catch (error2) {
32203
32527
  const msg = error2 instanceof Error ? error2.message : String(error2);
32204
32528
  error(`[turbo/lean/state] Failed to persist ${STATE_FILE3} atomically: ${msg}`);
32205
32529
  try {
32206
- if (fs31.existsSync(tmpPath)) {
32207
- fs31.unlinkSync(tmpPath);
32530
+ if (fs32.existsSync(tmpPath)) {
32531
+ fs32.unlinkSync(tmpPath);
32208
32532
  }
32209
32533
  } catch {}
32210
32534
  throw new Error(`Lean Turbo state persistence failed: ${msg}`);
@@ -32304,7 +32628,7 @@ var _internals45 = {
32304
32628
  };
32305
32629
  function readSpecStalenessSnapshot(directory) {
32306
32630
  try {
32307
- const p = path64.join(directory, ".swarm", "spec-staleness.json");
32631
+ const p = path65.join(directory, ".swarm", "spec-staleness.json");
32308
32632
  if (!fsSync3.existsSync(p))
32309
32633
  return { stale: false };
32310
32634
  const raw = fsSync3.readFileSync(p, "utf-8");
@@ -32836,13 +33160,13 @@ function buildStatusMessage2(session, directory, sessionID) {
32836
33160
  }
32837
33161
 
32838
33162
  // src/commands/unlink.ts
32839
- import { existsSync as existsSync37 } from "fs";
32840
- import * as path65 from "path";
33163
+ import { existsSync as existsSync38 } from "fs";
33164
+ import * as path66 from "path";
32841
33165
  var DEDUP_THRESHOLD2 = 0.6;
32842
33166
  async function copySharedKnowledgeToLocal(linkDir, localSwarmDir) {
32843
- const sharedPath = path65.join(linkDir, "knowledge.jsonl");
32844
- const localPath = path65.join(localSwarmDir, "knowledge.jsonl");
32845
- if (!existsSync37(sharedPath))
33167
+ const sharedPath = path66.join(linkDir, "knowledge.jsonl");
33168
+ const localPath = path66.join(localSwarmDir, "knowledge.jsonl");
33169
+ if (!existsSync38(sharedPath))
32846
33170
  return 0;
32847
33171
  const sharedEntries = await readKnowledge(sharedPath);
32848
33172
  if (sharedEntries.length === 0)
@@ -32876,7 +33200,7 @@ async function handleUnlinkCommand(directory, args) {
32876
33200
  let copied = 0;
32877
33201
  if (copyBack) {
32878
33202
  try {
32879
- copied = await copySharedKnowledgeToLocal(linkDir, path65.join(directory, ".swarm"));
33203
+ copied = await copySharedKnowledgeToLocal(linkDir, path66.join(directory, ".swarm"));
32880
33204
  } catch (error2) {
32881
33205
  return `\u274C Failed to copy shared knowledge back to local: ${error2 instanceof Error ? error2.message : String(error2)}`;
32882
33206
  }
@@ -33038,7 +33362,7 @@ function buildDetailedHelp(commandName, entry) {
33038
33362
  async function handleHelpCommand(ctx) {
33039
33363
  const targetCommand = ctx.args.join(" ");
33040
33364
  if (!targetCommand) {
33041
- const { buildHelpText } = await import("./index-jg6m72g7.js");
33365
+ const { buildHelpText } = await import("./index-mz3j0me8.js");
33042
33366
  return buildHelpText();
33043
33367
  }
33044
33368
  const tokens = targetCommand.split(/\s+/);
@@ -33047,7 +33371,7 @@ async function handleHelpCommand(ctx) {
33047
33371
  return _internals47.buildDetailedHelp(resolved.key, resolved.entry);
33048
33372
  }
33049
33373
  const similar = _internals47.findSimilarCommands(targetCommand);
33050
- const { buildHelpText: fullHelp } = await import("./index-jg6m72g7.js");
33374
+ const { buildHelpText: fullHelp } = await import("./index-mz3j0me8.js");
33051
33375
  if (similar.length > 0) {
33052
33376
  return `Command '/swarm ${targetCommand}' not found.
33053
33377
 
@@ -33180,7 +33504,7 @@ var COMMAND_REGISTRY = {
33180
33504
  },
33181
33505
  "guardrail explain": {
33182
33506
  handler: async (ctx) => {
33183
- const { handleGuardrailExplain } = await import("./guardrail-explain-q8pj9691.js");
33507
+ const { handleGuardrailExplain } = await import("./guardrail-explain-ma84cj3s.js");
33184
33508
  return handleGuardrailExplain(ctx.directory, ctx.args);
33185
33509
  },
33186
33510
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -33953,24 +34277,24 @@ function validateAliases() {
33953
34277
  continue;
33954
34278
  }
33955
34279
  const visited = new Set;
33956
- const path66 = [];
34280
+ const path67 = [];
33957
34281
  let current = target;
33958
34282
  while (current) {
33959
34283
  const currentEntry = COMMAND_REGISTRY[current];
33960
34284
  if (!currentEntry)
33961
34285
  break;
33962
34286
  if (visited.has(current)) {
33963
- const cycleStart = path66.indexOf(current);
34287
+ const cycleStart = path67.indexOf(current);
33964
34288
  const fullChain = [
33965
34289
  name,
33966
- ...path66.slice(0, cycleStart > 0 ? cycleStart : path66.length),
34290
+ ...path67.slice(0, cycleStart > 0 ? cycleStart : path67.length),
33967
34291
  current
33968
34292
  ].join(" \u2192 ");
33969
34293
  errors.push(`Circular alias detected: ${fullChain}`);
33970
34294
  break;
33971
34295
  }
33972
34296
  visited.add(current);
33973
- path66.push(current);
34297
+ path67.push(current);
33974
34298
  current = currentEntry.aliasOf || "";
33975
34299
  }
33976
34300
  }
@@ -12,7 +12,7 @@ import {
12
12
  detectPosixWrites,
13
13
  detectWindowsWrites,
14
14
  resolveWriteTargets
15
- } from "./index-q5cae5d7.js";
15
+ } from "./index-rmh6nqbp.js";
16
16
  import {
17
17
  checkFileAuthority,
18
18
  classifyFile,