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.
- package/dist/cli/{evidence-summary-service-ch74hb16.js → evidence-summary-service-c4d1c2t3.js} +1 -1
- package/dist/cli/{guardrail-explain-q8pj9691.js → guardrail-explain-ma84cj3s.js} +3 -3
- package/dist/cli/{index-y3x3gvy6.js → index-bnkd9ejn.js} +279 -22
- package/dist/cli/{index-jg6m72g7.js → index-mz3j0me8.js} +3 -3
- package/dist/cli/{index-q5cae5d7.js → index-rmh6nqbp.js} +375 -51
- package/dist/cli/{index-tj0jek4p.js → index-wc8g49dp.js} +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/commands/sdd.d.ts +2 -0
- package/dist/git/branch.d.ts +16 -0
- package/dist/index.js +1823 -1239
- package/dist/lang/runtime.d.ts +3 -0
- package/dist/sdd/effective-spec.d.ts +204 -3
- package/package.json +1 -1
|
@@ -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.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
|
|
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
|
-
|
|
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 ${
|
|
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-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
31897
|
-
|
|
31898
|
-
|
|
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
|
-
|
|
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
|
|
32065
|
-
const
|
|
32066
|
-
const reportPath =
|
|
32067
|
-
await
|
|
32068
|
-
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)}`);
|
|
32069
32393
|
try {
|
|
32070
|
-
await
|
|
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
|
|
32421
|
+
import * as path65 from "path";
|
|
32098
32422
|
|
|
32099
32423
|
// src/turbo/lean/state.ts
|
|
32100
32424
|
init_logger();
|
|
32101
|
-
import * as
|
|
32102
|
-
import * as
|
|
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 =
|
|
32109
|
-
if (!
|
|
32110
|
-
|
|
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 =
|
|
32154
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
32201
|
-
|
|
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 (
|
|
32207
|
-
|
|
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 =
|
|
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
|
|
32840
|
-
import * as
|
|
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 =
|
|
32844
|
-
const localPath =
|
|
32845
|
-
if (!
|
|
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,
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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 =
|
|
34287
|
+
const cycleStart = path67.indexOf(current);
|
|
33964
34288
|
const fullChain = [
|
|
33965
34289
|
name,
|
|
33966
|
-
...
|
|
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
|
-
|
|
34297
|
+
path67.push(current);
|
|
33974
34298
|
current = currentEntry.aliasOf || "";
|
|
33975
34299
|
}
|
|
33976
34300
|
}
|