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.
- package/.opencode/skills/execute/SKILL.md +1 -0
- package/.opencode/skills/running-tests/SKILL.md +1 -0
- package/.opencode/skills/writing-tests/SKILL.md +1 -0
- package/dist/cli/{evidence-summary-service-vgw9vw3n.js → evidence-summary-service-c4d1c2t3.js} +1 -1
- package/dist/cli/{guardrail-explain-z37rtfz4.js → guardrail-explain-ea9ja7x8.js} +3 -3
- package/dist/cli/{index-bcr2a549.js → index-2r26qngd.js} +3 -3
- package/dist/cli/{index-qvdxzjqt.js → index-3v3rq2tq.js} +337 -48
- package/dist/cli/{index-4xqnktvr.js → index-72893pwd.js} +1 -1
- package/dist/cli/{index-3emh7rny.js → index-bnkd9ejn.js} +276 -20
- package/dist/cli/index.js +2 -2
- package/dist/commands/sdd.d.ts +2 -0
- package/dist/index.js +2093 -1234
- package/dist/lang/symbol-graph.d.ts +2 -0
- package/dist/lang/symbol-visibility.d.ts +38 -0
- package/dist/sdd/effective-spec.d.ts +204 -3
- package/package.json +1 -1
|
@@ -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
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
handleGuardrailExplain
|
|
4
|
-
} from "./index-
|
|
5
|
-
import"./index-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
31932
|
-
|
|
31933
|
-
|
|
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
|
-
|
|
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
|
|
32100
|
-
const
|
|
32101
|
-
const reportPath =
|
|
32102
|
-
await
|
|
32103
|
-
const reportTempPath =
|
|
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
|
|
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
|
|
32421
|
+
import * as path65 from "path";
|
|
32133
32422
|
|
|
32134
32423
|
// src/turbo/lean/state.ts
|
|
32135
32424
|
init_logger();
|
|
32136
|
-
import * as
|
|
32137
|
-
import * as
|
|
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 =
|
|
32144
|
-
if (!
|
|
32145
|
-
|
|
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 =
|
|
32189
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
32236
|
-
|
|
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 (
|
|
32242
|
-
|
|
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 =
|
|
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
|
|
32875
|
-
import * as
|
|
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 =
|
|
32879
|
-
const localPath =
|
|
32880
|
-
if (!
|
|
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,
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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 =
|
|
34287
|
+
const cycleStart = path67.indexOf(current);
|
|
33999
34288
|
const fullChain = [
|
|
34000
34289
|
name,
|
|
34001
|
-
...
|
|
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
|
-
|
|
34297
|
+
path67.push(current);
|
|
34009
34298
|
current = currentEntry.aliasOf || "";
|
|
34010
34299
|
}
|
|
34011
34300
|
}
|