opencode-swarm 7.100.1 → 7.102.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.
@@ -54,6 +54,7 @@ All other gates: failure → return to coder. No self-fixes. No workarounds.
54
54
  → REQUIRED: Print "sast-baseline: [WRITTEN — N fingerprints | MERGED — N fingerprints | SKIPPED — gate disabled | ERROR — details]"
55
55
  → Subsequent `pre_check_batch` calls with `phase: <N>` will automatically diff against this baseline — only NEW findings (not in baseline) drive the fail verdict.
56
56
  5b. the active swarm's coder agent - Implement (if designer scaffold produced, include it as INPUT).
57
+ 5b-bis. **CODER OUTPUT VERIFICATION**: After the coder reports completion, do NOT accept the self-report alone. Run `diff` (step 5c) and inspect at least one of the modified files yourself to confirm the change exists. The coder may report DONE without having produced any diff. A 30-second read of the changed file(s) catches this failure mode. This is NOT a separate explorer dispatch — the existing `diff` tool at step 5c is the verification mechanism; the key discipline is checking that `diff` returns actual changes before proceeding, rather than forwarding the coder's self-report to the next gate.
57
58
  5c. Run `diff` tool. If `hasContractChanges` → the active swarm's explorer agent integration analysis. If COMPATIBILITY SIGNALS=INCOMPATIBLE or MIGRATION_SURFACE=yes → coder retry. If COMPATIBILITY SIGNALS=COMPATIBLE and MIGRATION_SURFACE=no → proceed.
58
59
  → REQUIRED: Print "diff: [PASS | CONTRACT CHANGE — details]"
59
60
  5d. Run `syntax_check` tool. SYNTACTIC ERRORS → return to coder. NO ERRORS → proceed to placeholder_scan.
@@ -137,6 +137,7 @@ Get-Content "$env:TEMP\test_out.txt" | Select-Object -Last 50
137
137
  - `Select-String -Last N` — invalid parameter, use `Select-Object -Last N`
138
138
  - `2>&1 2>&1` — duplicate redirection, causes parse error; use `2>&1` once
139
139
  - `&&` — not supported in PowerShell 5.1; use `; if ($?) { cmd2 }` instead
140
+ - `bun test --exec bash` — fails on Windows hosts with ENOENT (bash is not available in standard PowerShell). Use `bun test` directly or a PowerShell-based loop instead.
140
141
  - After `bun install --frozen-lockfile --force`, non-elevated Windows shells can hit `EPERM` while reading refreshed `node_modules` entries. Treat that as a host permission/access issue: rerun the same focused Bun command with approved/elevated access before diagnosing it as a code or test failure.
141
142
 
142
143
  ---
@@ -736,6 +736,7 @@ The `--timeout 120000` flag sets per-test timeout to 120 seconds. Individual tes
736
736
  3. Verify no `process.cwd()` usage — use the `directory` parameter from `createSwarmTool` or hook constructor
737
737
  4. Verify no hardcoded paths (`/tmp/...`, `C:\...`) — use `os.tmpdir()` + `path.join()`
738
738
  5. Verify mocks are restored in `afterEach` if using `spyOn` or `mock.module`
739
+ 6. Run `bunx @biomejs/biome@2.3.14 check --write <touched-test-files>` to auto-format only the files you created or modified. Formatting issues are a common first-pass failure — scoping the command to touched files avoids accidental workspace-wide rewrites.
739
740
 
740
741
  ## Known Pre-existing Test Failures
741
742
 
@@ -6,7 +6,7 @@ import {
6
6
  loadPlanJsonOnly,
7
7
  mergeDurableGateEntriesFromEvidence,
8
8
  readDurableGateEvidence
9
- } from "./index-3emh7rny.js";
9
+ } from "./index-bnkd9ejn.js";
10
10
  import"./index-r8f89sm9.js";
11
11
  import"./index-9b7qp18e.js";
12
12
  import"./index-7ztmmqpt.js";
@@ -1,15 +1,15 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-4xqnktvr.js";
5
- import"./index-qvdxzjqt.js";
4
+ } from "./index-72893pwd.js";
5
+ import"./index-3v3rq2tq.js";
6
6
  import"./index-pj3a5fbt.js";
7
7
  import"./index-ydgy7vg5.js";
8
8
  import"./index-vwyjkzgs.js";
9
9
  import"./index-f7nma02k.js";
10
10
  import"./index-rpb763g8.js";
11
11
  import"./index-m0qshbr6.js";
12
- import"./index-3emh7rny.js";
12
+ import"./index-bnkd9ejn.js";
13
13
  import"./index-adz3nk9b.js";
14
14
  import"./index-v4fcn4tr.js";
15
15
  import"./index-r8f89sm9.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-4xqnktvr.js";
4
+ } from "./index-72893pwd.js";
5
5
  import {
6
6
  handleGuardrailLog
7
7
  } from "./index-5bstvpct.js";
@@ -79,7 +79,7 @@ import {
79
79
  handleWriteRetroCommand,
80
80
  normalizeSwarmCommandInput,
81
81
  resolveCommand
82
- } from "./index-qvdxzjqt.js";
82
+ } from "./index-3v3rq2tq.js";
83
83
  import"./index-pj3a5fbt.js";
84
84
  import"./index-ydgy7vg5.js";
85
85
  import"./index-vwyjkzgs.js";
@@ -90,7 +90,7 @@ import {
90
90
  ORCHESTRATOR_NAME,
91
91
  stripKnownSwarmPrefix
92
92
  } from "./index-m0qshbr6.js";
93
- import"./index-3emh7rny.js";
93
+ import"./index-bnkd9ejn.js";
94
94
  import"./index-adz3nk9b.js";
95
95
  import"./index-v4fcn4tr.js";
96
96
  import"./index-r8f89sm9.js";
@@ -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-3emh7rny.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.1",
915
+ version: "7.102.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",
@@ -18002,7 +18005,7 @@ async function handleEvidenceCommand(directory, args) {
18002
18005
  return formatTaskEvidenceMarkdown(evidenceData);
18003
18006
  }
18004
18007
  async function handleEvidenceSummaryCommand(directory) {
18005
- const { buildEvidenceSummary } = await import("./evidence-summary-service-vgw9vw3n.js");
18008
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-c4d1c2t3.js");
18006
18009
  const artifact = await buildEvidenceSummary(directory);
18007
18010
  if (!artifact) {
18008
18011
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -31883,15 +31886,31 @@ async function handleRollbackCommand(directory, args) {
31883
31886
  }
31884
31887
 
31885
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");
31886
31892
  var USAGE9 = `Usage:
31887
- /swarm sdd status [--json]
31888
- /swarm sdd validate [--json] [--change <id>]
31889
- /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)
31890
31901
 
31891
31902
  OpenSpec-compatible SDD support:
31892
31903
  - reads checked-in openspec/specs and openspec/changes artifacts
31893
31904
  - produces one effective Swarm spec for planning
31894
- - 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
+ }
31895
31914
  function parseArgs10(args) {
31896
31915
  const parsed = { json: false, dryRun: false };
31897
31916
  for (let i = 0;i < args.length; i++) {
@@ -31905,13 +31924,37 @@ function parseArgs10(args) {
31905
31924
  return { ...parsed, error: "--change requires a value" };
31906
31925
  }
31907
31926
  const value = args[++i];
31908
- if (value.includes("/") || value.includes("\\") || value.includes("..") || value.includes("[") || value.includes("]")) {
31927
+ if (isUnsafeId(value)) {
31909
31928
  return {
31910
31929
  ...parsed,
31911
- 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"
31912
31931
  };
31913
31932
  }
31914
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;
31915
31958
  } else {
31916
31959
  return { ...parsed, error: `Unknown flag "${token}"` };
31917
31960
  }
@@ -31922,16 +31965,73 @@ function formatList(items) {
31922
31965
  return items.length > 0 ? items.map((item) => `- ${item}`).join(`
31923
31966
  `) : "- none";
31924
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
+ }
31925
31994
  async function handleSddStatusCommand(directory, args) {
31926
31995
  const parsed = parseArgs10(args);
31927
31996
  if (parsed.error)
31928
31997
  return `Error: ${parsed.error}
31929
31998
 
31930
31999
  ${USAGE9}`;
31931
- const status = loadSddStatusSync(directory);
31932
- if (parsed.json)
31933
- 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
+ }
31934
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
+ `);
31935
32035
  return [
31936
32036
  "## SDD Status",
31937
32037
  "",
@@ -31941,6 +32041,8 @@ ${USAGE9}`;
31941
32041
  `Current OpenSpec specs: ${status.currentSpecs.length}`,
31942
32042
  `Active OpenSpec changes: ${status.changes.length}`,
31943
32043
  "",
32044
+ speckitLines,
32045
+ "",
31944
32046
  "### Changes",
31945
32047
  formatList(changes),
31946
32048
  "",
@@ -31966,7 +32068,114 @@ async function handleSddValidateCommand(directory, args) {
31966
32068
  return `Error: ${parsed.error}
31967
32069
 
31968
32070
  ${USAGE9}`;
31969
- 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
+ });
31970
32179
  const projection = buildOpenSpecProjectionSync(directory, {
31971
32180
  changeId: parsed.changeId
31972
32181
  });
@@ -32008,6 +32217,86 @@ async function handleSddProjectCommand(directory, args) {
32008
32217
  return `Error: ${parsed.error}
32009
32218
 
32010
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
+ }
32011
32300
  const result = writeProjectedSpecSync(directory, {
32012
32301
  changeId: parsed.changeId,
32013
32302
  dryRun: parsed.dryRun
@@ -32096,13 +32385,13 @@ Ensure this is a git repository with commit history.`;
32096
32385
  const report = reportLines.filter(Boolean).join(`
32097
32386
  `);
32098
32387
  try {
32099
- const fs31 = await import("fs/promises");
32100
- const path63 = await import("path");
32101
- const reportPath = path63.join(directory, ".swarm", "simulate-report.md");
32102
- await fs31.mkdir(path63.dirname(reportPath), { recursive: true });
32103
- 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)}`);
32104
32393
  try {
32105
- await fs31.writeFile(reportTempPath, report, "utf-8");
32394
+ await fs32.writeFile(reportTempPath, report, "utf-8");
32106
32395
  renameSync11(reportTempPath, reportPath);
32107
32396
  } catch (err) {
32108
32397
  try {
@@ -32129,20 +32418,20 @@ async function handleSpecifyCommand(_directory, args) {
32129
32418
  // src/services/status-service.ts
32130
32419
  import * as fsSync3 from "fs";
32131
32420
  import { readFile as readFile16 } from "fs/promises";
32132
- import * as path64 from "path";
32421
+ import * as path65 from "path";
32133
32422
 
32134
32423
  // src/turbo/lean/state.ts
32135
32424
  init_logger();
32136
- import * as fs31 from "fs";
32137
- import * as path63 from "path";
32425
+ import * as fs32 from "fs";
32426
+ import * as path64 from "path";
32138
32427
  var STATE_FILE3 = "turbo-state.json";
32139
32428
  function nowISO3() {
32140
32429
  return new Date().toISOString();
32141
32430
  }
32142
32431
  function ensureSwarmDir2(directory) {
32143
- const swarmDir = path63.resolve(directory, ".swarm");
32144
- if (!fs31.existsSync(swarmDir)) {
32145
- fs31.mkdirSync(swarmDir, { recursive: true });
32432
+ const swarmDir = path64.resolve(directory, ".swarm");
32433
+ if (!fs32.existsSync(swarmDir)) {
32434
+ fs32.mkdirSync(swarmDir, { recursive: true });
32146
32435
  }
32147
32436
  return swarmDir;
32148
32437
  }
@@ -32185,17 +32474,17 @@ function markStateUnreadable2(directory, reason) {
32185
32474
  }
32186
32475
  function readPersisted2(directory) {
32187
32476
  try {
32188
- const filePath = path63.join(directory, ".swarm", STATE_FILE3);
32189
- if (!fs31.existsSync(filePath)) {
32477
+ const filePath = path64.join(directory, ".swarm", STATE_FILE3);
32478
+ if (!fs32.existsSync(filePath)) {
32190
32479
  const seed = emptyPersisted2();
32191
32480
  try {
32192
32481
  ensureSwarmDir2(directory);
32193
- fs31.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
32482
+ fs32.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
32194
32483
  `, "utf-8");
32195
32484
  } catch {}
32196
32485
  return seed;
32197
32486
  }
32198
- const raw = fs31.readFileSync(filePath, "utf-8");
32487
+ const raw = fs32.readFileSync(filePath, "utf-8");
32199
32488
  const parsed = JSON.parse(raw);
32200
32489
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
32201
32490
  markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
@@ -32221,7 +32510,7 @@ function writePersisted2(directory, persisted) {
32221
32510
  let payload;
32222
32511
  try {
32223
32512
  ensureSwarmDir2(directory);
32224
- filePath = path63.join(directory, ".swarm", STATE_FILE3);
32513
+ filePath = path64.join(directory, ".swarm", STATE_FILE3);
32225
32514
  tmpPath = `${filePath}.tmp.${Date.now()}`;
32226
32515
  persisted.updatedAt = nowISO3();
32227
32516
  payload = `${JSON.stringify(persisted, null, 2)}
@@ -32232,14 +32521,14 @@ function writePersisted2(directory, persisted) {
32232
32521
  throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
32233
32522
  }
32234
32523
  try {
32235
- fs31.writeFileSync(tmpPath, payload, "utf-8");
32236
- fs31.renameSync(tmpPath, filePath);
32524
+ fs32.writeFileSync(tmpPath, payload, "utf-8");
32525
+ fs32.renameSync(tmpPath, filePath);
32237
32526
  } catch (error2) {
32238
32527
  const msg = error2 instanceof Error ? error2.message : String(error2);
32239
32528
  error(`[turbo/lean/state] Failed to persist ${STATE_FILE3} atomically: ${msg}`);
32240
32529
  try {
32241
- if (fs31.existsSync(tmpPath)) {
32242
- fs31.unlinkSync(tmpPath);
32530
+ if (fs32.existsSync(tmpPath)) {
32531
+ fs32.unlinkSync(tmpPath);
32243
32532
  }
32244
32533
  } catch {}
32245
32534
  throw new Error(`Lean Turbo state persistence failed: ${msg}`);
@@ -32339,7 +32628,7 @@ var _internals45 = {
32339
32628
  };
32340
32629
  function readSpecStalenessSnapshot(directory) {
32341
32630
  try {
32342
- const p = path64.join(directory, ".swarm", "spec-staleness.json");
32631
+ const p = path65.join(directory, ".swarm", "spec-staleness.json");
32343
32632
  if (!fsSync3.existsSync(p))
32344
32633
  return { stale: false };
32345
32634
  const raw = fsSync3.readFileSync(p, "utf-8");
@@ -32871,13 +33160,13 @@ function buildStatusMessage2(session, directory, sessionID) {
32871
33160
  }
32872
33161
 
32873
33162
  // src/commands/unlink.ts
32874
- import { existsSync as existsSync37 } from "fs";
32875
- import * as path65 from "path";
33163
+ import { existsSync as existsSync38 } from "fs";
33164
+ import * as path66 from "path";
32876
33165
  var DEDUP_THRESHOLD2 = 0.6;
32877
33166
  async function copySharedKnowledgeToLocal(linkDir, localSwarmDir) {
32878
- const sharedPath = path65.join(linkDir, "knowledge.jsonl");
32879
- const localPath = path65.join(localSwarmDir, "knowledge.jsonl");
32880
- if (!existsSync37(sharedPath))
33167
+ const sharedPath = path66.join(linkDir, "knowledge.jsonl");
33168
+ const localPath = path66.join(localSwarmDir, "knowledge.jsonl");
33169
+ if (!existsSync38(sharedPath))
32881
33170
  return 0;
32882
33171
  const sharedEntries = await readKnowledge(sharedPath);
32883
33172
  if (sharedEntries.length === 0)
@@ -32911,7 +33200,7 @@ async function handleUnlinkCommand(directory, args) {
32911
33200
  let copied = 0;
32912
33201
  if (copyBack) {
32913
33202
  try {
32914
- copied = await copySharedKnowledgeToLocal(linkDir, path65.join(directory, ".swarm"));
33203
+ copied = await copySharedKnowledgeToLocal(linkDir, path66.join(directory, ".swarm"));
32915
33204
  } catch (error2) {
32916
33205
  return `\u274C Failed to copy shared knowledge back to local: ${error2 instanceof Error ? error2.message : String(error2)}`;
32917
33206
  }
@@ -33073,7 +33362,7 @@ function buildDetailedHelp(commandName, entry) {
33073
33362
  async function handleHelpCommand(ctx) {
33074
33363
  const targetCommand = ctx.args.join(" ");
33075
33364
  if (!targetCommand) {
33076
- const { buildHelpText } = await import("./index-bcr2a549.js");
33365
+ const { buildHelpText } = await import("./index-2r26qngd.js");
33077
33366
  return buildHelpText();
33078
33367
  }
33079
33368
  const tokens = targetCommand.split(/\s+/);
@@ -33082,7 +33371,7 @@ async function handleHelpCommand(ctx) {
33082
33371
  return _internals47.buildDetailedHelp(resolved.key, resolved.entry);
33083
33372
  }
33084
33373
  const similar = _internals47.findSimilarCommands(targetCommand);
33085
- const { buildHelpText: fullHelp } = await import("./index-bcr2a549.js");
33374
+ const { buildHelpText: fullHelp } = await import("./index-2r26qngd.js");
33086
33375
  if (similar.length > 0) {
33087
33376
  return `Command '/swarm ${targetCommand}' not found.
33088
33377
 
@@ -33215,7 +33504,7 @@ var COMMAND_REGISTRY = {
33215
33504
  },
33216
33505
  "guardrail explain": {
33217
33506
  handler: async (ctx) => {
33218
- const { handleGuardrailExplain } = await import("./guardrail-explain-z37rtfz4.js");
33507
+ const { handleGuardrailExplain } = await import("./guardrail-explain-ea9ja7x8.js");
33219
33508
  return handleGuardrailExplain(ctx.directory, ctx.args);
33220
33509
  },
33221
33510
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -33988,24 +34277,24 @@ function validateAliases() {
33988
34277
  continue;
33989
34278
  }
33990
34279
  const visited = new Set;
33991
- const path66 = [];
34280
+ const path67 = [];
33992
34281
  let current = target;
33993
34282
  while (current) {
33994
34283
  const currentEntry = COMMAND_REGISTRY[current];
33995
34284
  if (!currentEntry)
33996
34285
  break;
33997
34286
  if (visited.has(current)) {
33998
- const cycleStart = path66.indexOf(current);
34287
+ const cycleStart = path67.indexOf(current);
33999
34288
  const fullChain = [
34000
34289
  name,
34001
- ...path66.slice(0, cycleStart > 0 ? cycleStart : path66.length),
34290
+ ...path67.slice(0, cycleStart > 0 ? cycleStart : path67.length),
34002
34291
  current
34003
34292
  ].join(" \u2192 ");
34004
34293
  errors.push(`Circular alias detected: ${fullChain}`);
34005
34294
  break;
34006
34295
  }
34007
34296
  visited.add(current);
34008
- path66.push(current);
34297
+ path67.push(current);
34009
34298
  current = currentEntry.aliasOf || "";
34010
34299
  }
34011
34300
  }