opencode-swarm 6.41.2 → 6.41.4
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/index.js +247 -160
- package/dist/index.js +765 -612
- package/dist/tools/completion-verify.d.ts +2 -0
- package/dist/tools/resolve-working-directory.d.ts +35 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -17531,9 +17531,9 @@ var init_evidence_summary_service = __esm(() => {
|
|
|
17531
17531
|
});
|
|
17532
17532
|
|
|
17533
17533
|
// src/cli/index.ts
|
|
17534
|
-
import * as
|
|
17534
|
+
import * as fs15 from "fs";
|
|
17535
17535
|
import * as os5 from "os";
|
|
17536
|
-
import * as
|
|
17536
|
+
import * as path25 from "path";
|
|
17537
17537
|
|
|
17538
17538
|
// src/commands/agents.ts
|
|
17539
17539
|
function handleAgentsCommand(agents, guardrails) {
|
|
@@ -33432,7 +33432,7 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
|
|
|
33432
33432
|
lesson,
|
|
33433
33433
|
category: "architecture",
|
|
33434
33434
|
tags: ["hidden-coupling", "co-change", "dark-matter"],
|
|
33435
|
-
scope: "
|
|
33435
|
+
scope: "global",
|
|
33436
33436
|
confidence,
|
|
33437
33437
|
status: "candidate",
|
|
33438
33438
|
confirmed_by: [],
|
|
@@ -35418,8 +35418,8 @@ async function handlePlanCommand(directory, args) {
|
|
|
35418
35418
|
// src/services/preflight-service.ts
|
|
35419
35419
|
init_manager();
|
|
35420
35420
|
init_manager2();
|
|
35421
|
-
import * as
|
|
35422
|
-
import * as
|
|
35421
|
+
import * as fs11 from "fs";
|
|
35422
|
+
import * as path21 from "path";
|
|
35423
35423
|
|
|
35424
35424
|
// src/tools/lint.ts
|
|
35425
35425
|
import * as fs7 from "fs";
|
|
@@ -37804,8 +37804,52 @@ async function runSecretscan(directory) {
|
|
|
37804
37804
|
}
|
|
37805
37805
|
|
|
37806
37806
|
// src/tools/test-runner.ts
|
|
37807
|
+
import * as fs10 from "fs";
|
|
37808
|
+
import * as path20 from "path";
|
|
37809
|
+
|
|
37810
|
+
// src/tools/resolve-working-directory.ts
|
|
37807
37811
|
import * as fs9 from "fs";
|
|
37808
37812
|
import * as path19 from "path";
|
|
37813
|
+
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
37814
|
+
if (workingDirectory == null || workingDirectory === "") {
|
|
37815
|
+
return { success: true, directory: fallbackDirectory };
|
|
37816
|
+
}
|
|
37817
|
+
if (workingDirectory.includes("\x00")) {
|
|
37818
|
+
return {
|
|
37819
|
+
success: false,
|
|
37820
|
+
message: "Invalid working_directory: null bytes are not allowed"
|
|
37821
|
+
};
|
|
37822
|
+
}
|
|
37823
|
+
if (process.platform === "win32") {
|
|
37824
|
+
const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
|
|
37825
|
+
if (devicePathPattern.test(workingDirectory)) {
|
|
37826
|
+
return {
|
|
37827
|
+
success: false,
|
|
37828
|
+
message: "Invalid working_directory: Windows device paths are not allowed"
|
|
37829
|
+
};
|
|
37830
|
+
}
|
|
37831
|
+
}
|
|
37832
|
+
const normalizedDir = path19.normalize(workingDirectory);
|
|
37833
|
+
const pathParts = normalizedDir.split(path19.sep);
|
|
37834
|
+
if (pathParts.includes("..")) {
|
|
37835
|
+
return {
|
|
37836
|
+
success: false,
|
|
37837
|
+
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
37838
|
+
};
|
|
37839
|
+
}
|
|
37840
|
+
const resolvedDir = path19.resolve(normalizedDir);
|
|
37841
|
+
try {
|
|
37842
|
+
const realPath = fs9.realpathSync(resolvedDir);
|
|
37843
|
+
return { success: true, directory: realPath };
|
|
37844
|
+
} catch {
|
|
37845
|
+
return {
|
|
37846
|
+
success: false,
|
|
37847
|
+
message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
|
|
37848
|
+
};
|
|
37849
|
+
}
|
|
37850
|
+
}
|
|
37851
|
+
|
|
37852
|
+
// src/tools/test-runner.ts
|
|
37809
37853
|
var MAX_OUTPUT_BYTES3 = 512000;
|
|
37810
37854
|
var MAX_COMMAND_LENGTH2 = 500;
|
|
37811
37855
|
var DEFAULT_TIMEOUT_MS = 60000;
|
|
@@ -37876,19 +37920,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
37876
37920
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
37877
37921
|
}
|
|
37878
37922
|
function detectGoTest(cwd) {
|
|
37879
|
-
return
|
|
37923
|
+
return fs10.existsSync(path20.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
37880
37924
|
}
|
|
37881
37925
|
function detectJavaMaven(cwd) {
|
|
37882
|
-
return
|
|
37926
|
+
return fs10.existsSync(path20.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
37883
37927
|
}
|
|
37884
37928
|
function detectGradle(cwd) {
|
|
37885
|
-
const hasBuildFile =
|
|
37886
|
-
const hasGradlew =
|
|
37929
|
+
const hasBuildFile = fs10.existsSync(path20.join(cwd, "build.gradle")) || fs10.existsSync(path20.join(cwd, "build.gradle.kts"));
|
|
37930
|
+
const hasGradlew = fs10.existsSync(path20.join(cwd, "gradlew")) || fs10.existsSync(path20.join(cwd, "gradlew.bat"));
|
|
37887
37931
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
37888
37932
|
}
|
|
37889
37933
|
function detectDotnetTest(cwd) {
|
|
37890
37934
|
try {
|
|
37891
|
-
const files =
|
|
37935
|
+
const files = fs10.readdirSync(cwd);
|
|
37892
37936
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
37893
37937
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
37894
37938
|
} catch {
|
|
@@ -37896,32 +37940,32 @@ function detectDotnetTest(cwd) {
|
|
|
37896
37940
|
}
|
|
37897
37941
|
}
|
|
37898
37942
|
function detectCTest(cwd) {
|
|
37899
|
-
const hasSource =
|
|
37900
|
-
const hasBuildCache =
|
|
37943
|
+
const hasSource = fs10.existsSync(path20.join(cwd, "CMakeLists.txt"));
|
|
37944
|
+
const hasBuildCache = fs10.existsSync(path20.join(cwd, "CMakeCache.txt")) || fs10.existsSync(path20.join(cwd, "build", "CMakeCache.txt"));
|
|
37901
37945
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
37902
37946
|
}
|
|
37903
37947
|
function detectSwiftTest(cwd) {
|
|
37904
|
-
return
|
|
37948
|
+
return fs10.existsSync(path20.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
37905
37949
|
}
|
|
37906
37950
|
function detectDartTest(cwd) {
|
|
37907
|
-
return
|
|
37951
|
+
return fs10.existsSync(path20.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
37908
37952
|
}
|
|
37909
37953
|
function detectRSpec(cwd) {
|
|
37910
|
-
const hasRSpecFile =
|
|
37911
|
-
const hasGemfile =
|
|
37912
|
-
const hasSpecDir =
|
|
37954
|
+
const hasRSpecFile = fs10.existsSync(path20.join(cwd, ".rspec"));
|
|
37955
|
+
const hasGemfile = fs10.existsSync(path20.join(cwd, "Gemfile"));
|
|
37956
|
+
const hasSpecDir = fs10.existsSync(path20.join(cwd, "spec"));
|
|
37913
37957
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
37914
37958
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
37915
37959
|
}
|
|
37916
37960
|
function detectMinitest(cwd) {
|
|
37917
|
-
return
|
|
37961
|
+
return fs10.existsSync(path20.join(cwd, "test")) && (fs10.existsSync(path20.join(cwd, "Gemfile")) || fs10.existsSync(path20.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
37918
37962
|
}
|
|
37919
37963
|
async function detectTestFramework(cwd) {
|
|
37920
37964
|
const baseDir = cwd;
|
|
37921
37965
|
try {
|
|
37922
|
-
const packageJsonPath =
|
|
37923
|
-
if (
|
|
37924
|
-
const content =
|
|
37966
|
+
const packageJsonPath = path20.join(baseDir, "package.json");
|
|
37967
|
+
if (fs10.existsSync(packageJsonPath)) {
|
|
37968
|
+
const content = fs10.readFileSync(packageJsonPath, "utf-8");
|
|
37925
37969
|
const pkg = JSON.parse(content);
|
|
37926
37970
|
const _deps = pkg.dependencies || {};
|
|
37927
37971
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -37940,38 +37984,38 @@ async function detectTestFramework(cwd) {
|
|
|
37940
37984
|
return "jest";
|
|
37941
37985
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
37942
37986
|
return "mocha";
|
|
37943
|
-
if (
|
|
37987
|
+
if (fs10.existsSync(path20.join(baseDir, "bun.lockb")) || fs10.existsSync(path20.join(baseDir, "bun.lock"))) {
|
|
37944
37988
|
if (scripts.test?.includes("bun"))
|
|
37945
37989
|
return "bun";
|
|
37946
37990
|
}
|
|
37947
37991
|
}
|
|
37948
37992
|
} catch {}
|
|
37949
37993
|
try {
|
|
37950
|
-
const pyprojectTomlPath =
|
|
37951
|
-
const setupCfgPath =
|
|
37952
|
-
const requirementsTxtPath =
|
|
37953
|
-
if (
|
|
37954
|
-
const content =
|
|
37994
|
+
const pyprojectTomlPath = path20.join(baseDir, "pyproject.toml");
|
|
37995
|
+
const setupCfgPath = path20.join(baseDir, "setup.cfg");
|
|
37996
|
+
const requirementsTxtPath = path20.join(baseDir, "requirements.txt");
|
|
37997
|
+
if (fs10.existsSync(pyprojectTomlPath)) {
|
|
37998
|
+
const content = fs10.readFileSync(pyprojectTomlPath, "utf-8");
|
|
37955
37999
|
if (content.includes("[tool.pytest"))
|
|
37956
38000
|
return "pytest";
|
|
37957
38001
|
if (content.includes("pytest"))
|
|
37958
38002
|
return "pytest";
|
|
37959
38003
|
}
|
|
37960
|
-
if (
|
|
37961
|
-
const content =
|
|
38004
|
+
if (fs10.existsSync(setupCfgPath)) {
|
|
38005
|
+
const content = fs10.readFileSync(setupCfgPath, "utf-8");
|
|
37962
38006
|
if (content.includes("[pytest]"))
|
|
37963
38007
|
return "pytest";
|
|
37964
38008
|
}
|
|
37965
|
-
if (
|
|
37966
|
-
const content =
|
|
38009
|
+
if (fs10.existsSync(requirementsTxtPath)) {
|
|
38010
|
+
const content = fs10.readFileSync(requirementsTxtPath, "utf-8");
|
|
37967
38011
|
if (content.includes("pytest"))
|
|
37968
38012
|
return "pytest";
|
|
37969
38013
|
}
|
|
37970
38014
|
} catch {}
|
|
37971
38015
|
try {
|
|
37972
|
-
const cargoTomlPath =
|
|
37973
|
-
if (
|
|
37974
|
-
const content =
|
|
38016
|
+
const cargoTomlPath = path20.join(baseDir, "Cargo.toml");
|
|
38017
|
+
if (fs10.existsSync(cargoTomlPath)) {
|
|
38018
|
+
const content = fs10.readFileSync(cargoTomlPath, "utf-8");
|
|
37975
38019
|
if (content.includes("[dev-dependencies]")) {
|
|
37976
38020
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
37977
38021
|
return "cargo";
|
|
@@ -37980,10 +38024,10 @@ async function detectTestFramework(cwd) {
|
|
|
37980
38024
|
}
|
|
37981
38025
|
} catch {}
|
|
37982
38026
|
try {
|
|
37983
|
-
const pesterConfigPath =
|
|
37984
|
-
const pesterConfigJsonPath =
|
|
37985
|
-
const pesterPs1Path =
|
|
37986
|
-
if (
|
|
38027
|
+
const pesterConfigPath = path20.join(baseDir, "pester.config.ps1");
|
|
38028
|
+
const pesterConfigJsonPath = path20.join(baseDir, "pester.config.ps1.json");
|
|
38029
|
+
const pesterPs1Path = path20.join(baseDir, "tests.ps1");
|
|
38030
|
+
if (fs10.existsSync(pesterConfigPath) || fs10.existsSync(pesterConfigJsonPath) || fs10.existsSync(pesterPs1Path)) {
|
|
37987
38031
|
return "pester";
|
|
37988
38032
|
}
|
|
37989
38033
|
} catch {}
|
|
@@ -38034,8 +38078,8 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38034
38078
|
const testFiles = [];
|
|
38035
38079
|
for (const file3 of sourceFiles) {
|
|
38036
38080
|
const normalizedPath = file3.replace(/\\/g, "/");
|
|
38037
|
-
const basename4 =
|
|
38038
|
-
const dirname9 =
|
|
38081
|
+
const basename4 = path20.basename(file3);
|
|
38082
|
+
const dirname9 = path20.dirname(file3);
|
|
38039
38083
|
if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
|
|
38040
38084
|
if (!testFiles.includes(file3)) {
|
|
38041
38085
|
testFiles.push(file3);
|
|
@@ -38044,16 +38088,16 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38044
38088
|
}
|
|
38045
38089
|
for (const _pattern of TEST_PATTERNS) {
|
|
38046
38090
|
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
38047
|
-
const ext =
|
|
38091
|
+
const ext = path20.extname(basename4);
|
|
38048
38092
|
const possibleTestFiles = [
|
|
38049
|
-
|
|
38050
|
-
|
|
38051
|
-
|
|
38052
|
-
|
|
38053
|
-
|
|
38093
|
+
path20.join(dirname9, `${nameWithoutExt}.spec${ext}`),
|
|
38094
|
+
path20.join(dirname9, `${nameWithoutExt}.test${ext}`),
|
|
38095
|
+
path20.join(dirname9, "__tests__", `${nameWithoutExt}${ext}`),
|
|
38096
|
+
path20.join(dirname9, "tests", `${nameWithoutExt}${ext}`),
|
|
38097
|
+
path20.join(dirname9, "test", `${nameWithoutExt}${ext}`)
|
|
38054
38098
|
];
|
|
38055
38099
|
for (const testFile of possibleTestFiles) {
|
|
38056
|
-
if (
|
|
38100
|
+
if (fs10.existsSync(testFile) && !testFiles.includes(testFile)) {
|
|
38057
38101
|
testFiles.push(testFile);
|
|
38058
38102
|
}
|
|
38059
38103
|
}
|
|
@@ -38069,8 +38113,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38069
38113
|
}
|
|
38070
38114
|
for (const testFile of candidateTestFiles) {
|
|
38071
38115
|
try {
|
|
38072
|
-
const content =
|
|
38073
|
-
const testDir =
|
|
38116
|
+
const content = fs10.readFileSync(testFile, "utf-8");
|
|
38117
|
+
const testDir = path20.dirname(testFile);
|
|
38074
38118
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
38075
38119
|
let match;
|
|
38076
38120
|
match = importRegex.exec(content);
|
|
@@ -38078,8 +38122,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38078
38122
|
const importPath = match[1];
|
|
38079
38123
|
let resolvedImport;
|
|
38080
38124
|
if (importPath.startsWith(".")) {
|
|
38081
|
-
resolvedImport =
|
|
38082
|
-
const existingExt =
|
|
38125
|
+
resolvedImport = path20.resolve(testDir, importPath);
|
|
38126
|
+
const existingExt = path20.extname(resolvedImport);
|
|
38083
38127
|
if (!existingExt) {
|
|
38084
38128
|
for (const extToTry of [
|
|
38085
38129
|
".ts",
|
|
@@ -38090,7 +38134,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38090
38134
|
".cjs"
|
|
38091
38135
|
]) {
|
|
38092
38136
|
const withExt = resolvedImport + extToTry;
|
|
38093
|
-
if (sourceFiles.includes(withExt) ||
|
|
38137
|
+
if (sourceFiles.includes(withExt) || fs10.existsSync(withExt)) {
|
|
38094
38138
|
resolvedImport = withExt;
|
|
38095
38139
|
break;
|
|
38096
38140
|
}
|
|
@@ -38099,12 +38143,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38099
38143
|
} else {
|
|
38100
38144
|
continue;
|
|
38101
38145
|
}
|
|
38102
|
-
const importBasename =
|
|
38103
|
-
const importDir =
|
|
38146
|
+
const importBasename = path20.basename(resolvedImport, path20.extname(resolvedImport));
|
|
38147
|
+
const importDir = path20.dirname(resolvedImport);
|
|
38104
38148
|
for (const sourceFile of sourceFiles) {
|
|
38105
|
-
const sourceDir =
|
|
38106
|
-
const sourceBasename =
|
|
38107
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38149
|
+
const sourceDir = path20.dirname(sourceFile);
|
|
38150
|
+
const sourceBasename = path20.basename(sourceFile, path20.extname(sourceFile));
|
|
38151
|
+
const isRelatedDir = importDir === sourceDir || importDir === path20.join(sourceDir, "__tests__") || importDir === path20.join(sourceDir, "tests") || importDir === path20.join(sourceDir, "test");
|
|
38108
38152
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38109
38153
|
if (!testFiles.includes(testFile)) {
|
|
38110
38154
|
testFiles.push(testFile);
|
|
@@ -38119,8 +38163,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38119
38163
|
while (match !== null) {
|
|
38120
38164
|
const importPath = match[1];
|
|
38121
38165
|
if (importPath.startsWith(".")) {
|
|
38122
|
-
let resolvedImport =
|
|
38123
|
-
const existingExt =
|
|
38166
|
+
let resolvedImport = path20.resolve(testDir, importPath);
|
|
38167
|
+
const existingExt = path20.extname(resolvedImport);
|
|
38124
38168
|
if (!existingExt) {
|
|
38125
38169
|
for (const extToTry of [
|
|
38126
38170
|
".ts",
|
|
@@ -38131,18 +38175,18 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38131
38175
|
".cjs"
|
|
38132
38176
|
]) {
|
|
38133
38177
|
const withExt = resolvedImport + extToTry;
|
|
38134
|
-
if (sourceFiles.includes(withExt) ||
|
|
38178
|
+
if (sourceFiles.includes(withExt) || fs10.existsSync(withExt)) {
|
|
38135
38179
|
resolvedImport = withExt;
|
|
38136
38180
|
break;
|
|
38137
38181
|
}
|
|
38138
38182
|
}
|
|
38139
38183
|
}
|
|
38140
|
-
const importDir =
|
|
38141
|
-
const importBasename =
|
|
38184
|
+
const importDir = path20.dirname(resolvedImport);
|
|
38185
|
+
const importBasename = path20.basename(resolvedImport, path20.extname(resolvedImport));
|
|
38142
38186
|
for (const sourceFile of sourceFiles) {
|
|
38143
|
-
const sourceDir =
|
|
38144
|
-
const sourceBasename =
|
|
38145
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38187
|
+
const sourceDir = path20.dirname(sourceFile);
|
|
38188
|
+
const sourceBasename = path20.basename(sourceFile, path20.extname(sourceFile));
|
|
38189
|
+
const isRelatedDir = importDir === sourceDir || importDir === path20.join(sourceDir, "__tests__") || importDir === path20.join(sourceDir, "tests") || importDir === path20.join(sourceDir, "test");
|
|
38146
38190
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38147
38191
|
if (!testFiles.includes(testFile)) {
|
|
38148
38192
|
testFiles.push(testFile);
|
|
@@ -38227,8 +38271,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38227
38271
|
return ["mvn", "test"];
|
|
38228
38272
|
case "gradle": {
|
|
38229
38273
|
const isWindows = process.platform === "win32";
|
|
38230
|
-
const hasGradlewBat =
|
|
38231
|
-
const hasGradlew =
|
|
38274
|
+
const hasGradlewBat = fs10.existsSync(path20.join(baseDir, "gradlew.bat"));
|
|
38275
|
+
const hasGradlew = fs10.existsSync(path20.join(baseDir, "gradlew"));
|
|
38232
38276
|
if (hasGradlewBat && isWindows)
|
|
38233
38277
|
return ["gradlew.bat", "test"];
|
|
38234
38278
|
if (hasGradlew)
|
|
@@ -38245,7 +38289,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38245
38289
|
"cmake-build-release",
|
|
38246
38290
|
"out"
|
|
38247
38291
|
];
|
|
38248
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
38292
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs10.existsSync(path20.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
38249
38293
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
38250
38294
|
}
|
|
38251
38295
|
case "swift-test":
|
|
@@ -38477,6 +38521,43 @@ function parseTestOutput(framework, output) {
|
|
|
38477
38521
|
}
|
|
38478
38522
|
return { totals, coveragePercent };
|
|
38479
38523
|
}
|
|
38524
|
+
async function readBoundedStream(stream, maxBytes) {
|
|
38525
|
+
const reader = stream.getReader();
|
|
38526
|
+
const chunks = [];
|
|
38527
|
+
let totalBytes = 0;
|
|
38528
|
+
let truncated = false;
|
|
38529
|
+
try {
|
|
38530
|
+
while (true) {
|
|
38531
|
+
const { done, value } = await reader.read();
|
|
38532
|
+
if (done)
|
|
38533
|
+
break;
|
|
38534
|
+
if (totalBytes + value.length > maxBytes) {
|
|
38535
|
+
const remaining = maxBytes - totalBytes;
|
|
38536
|
+
if (remaining > 0) {
|
|
38537
|
+
chunks.push(value.slice(0, remaining));
|
|
38538
|
+
}
|
|
38539
|
+
totalBytes = maxBytes;
|
|
38540
|
+
truncated = true;
|
|
38541
|
+
reader.cancel().catch(() => {});
|
|
38542
|
+
break;
|
|
38543
|
+
}
|
|
38544
|
+
chunks.push(value);
|
|
38545
|
+
totalBytes += value.length;
|
|
38546
|
+
}
|
|
38547
|
+
} catch {} finally {
|
|
38548
|
+
try {
|
|
38549
|
+
reader.releaseLock();
|
|
38550
|
+
} catch {}
|
|
38551
|
+
}
|
|
38552
|
+
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
38553
|
+
const combined = new Uint8Array(totalBytes);
|
|
38554
|
+
let offset = 0;
|
|
38555
|
+
for (const chunk of chunks) {
|
|
38556
|
+
combined.set(chunk, offset);
|
|
38557
|
+
offset += chunk.length;
|
|
38558
|
+
}
|
|
38559
|
+
return { text: decoder.decode(combined), truncated };
|
|
38560
|
+
}
|
|
38480
38561
|
async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
38481
38562
|
const command = buildTestCommand(framework, scope, files, coverage, cwd);
|
|
38482
38563
|
if (!command) {
|
|
@@ -38505,34 +38586,24 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
38505
38586
|
stderr: "pipe",
|
|
38506
38587
|
cwd
|
|
38507
38588
|
});
|
|
38508
|
-
const
|
|
38509
|
-
const timeoutPromise = new Promise((resolve7) => setTimeout(() => {
|
|
38589
|
+
const timeoutPromise = new Promise((resolve8) => setTimeout(() => {
|
|
38510
38590
|
proc.kill();
|
|
38511
|
-
|
|
38591
|
+
resolve8(-1);
|
|
38512
38592
|
}, timeout_ms));
|
|
38513
|
-
const exitCode = await Promise.
|
|
38514
|
-
|
|
38515
|
-
|
|
38516
|
-
|
|
38517
|
-
new Response(proc.stderr).text()
|
|
38593
|
+
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
38594
|
+
Promise.race([proc.exited, timeoutPromise]),
|
|
38595
|
+
readBoundedStream(proc.stdout, MAX_OUTPUT_BYTES3),
|
|
38596
|
+
readBoundedStream(proc.stderr, MAX_OUTPUT_BYTES3)
|
|
38518
38597
|
]);
|
|
38519
|
-
|
|
38520
|
-
|
|
38598
|
+
const duration_ms = Date.now() - startTime;
|
|
38599
|
+
let output = stdoutResult.text;
|
|
38600
|
+
if (stderrResult.text) {
|
|
38521
38601
|
output += (output ? `
|
|
38522
|
-
` : "") +
|
|
38602
|
+
` : "") + stderrResult.text;
|
|
38523
38603
|
}
|
|
38524
|
-
|
|
38525
|
-
|
|
38526
|
-
|
|
38527
|
-
while (truncIndex > 0) {
|
|
38528
|
-
const truncated = output.slice(0, truncIndex);
|
|
38529
|
-
if (Buffer.byteLength(truncated, "utf-8") <= MAX_OUTPUT_BYTES3) {
|
|
38530
|
-
break;
|
|
38531
|
-
}
|
|
38532
|
-
truncIndex--;
|
|
38533
|
-
}
|
|
38534
|
-
output = `${output.slice(0, truncIndex)}
|
|
38535
|
-
... (output truncated)`;
|
|
38604
|
+
if (stdoutResult.truncated || stderrResult.truncated) {
|
|
38605
|
+
output += `
|
|
38606
|
+
... (output truncated at stream read limit)`;
|
|
38536
38607
|
}
|
|
38537
38608
|
const { totals, coveragePercent } = parseTestOutput(framework, output);
|
|
38538
38609
|
const isTimeout = exitCode === -1;
|
|
@@ -38646,10 +38717,26 @@ var test_runner = createSwarmTool({
|
|
|
38646
38717
|
files: tool.schema.array(tool.schema.string()).optional().describe("Specific files to test (used with convention or graph scope)"),
|
|
38647
38718
|
coverage: tool.schema.boolean().optional().describe("Enable coverage reporting if supported"),
|
|
38648
38719
|
timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
|
|
38649
|
-
allow_full_suite: tool.schema.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.')
|
|
38720
|
+
allow_full_suite: tool.schema.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
|
|
38721
|
+
working_directory: tool.schema.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
|
|
38650
38722
|
},
|
|
38651
38723
|
async execute(args, directory) {
|
|
38652
|
-
|
|
38724
|
+
let workingDirInput;
|
|
38725
|
+
if (args && typeof args === "object") {
|
|
38726
|
+
const obj = args;
|
|
38727
|
+
workingDirInput = typeof obj.working_directory === "string" ? obj.working_directory : undefined;
|
|
38728
|
+
}
|
|
38729
|
+
const dirResult = resolveWorkingDirectory(workingDirInput, directory);
|
|
38730
|
+
if (!dirResult.success) {
|
|
38731
|
+
const errorResult = {
|
|
38732
|
+
success: false,
|
|
38733
|
+
framework: "none",
|
|
38734
|
+
scope: "all",
|
|
38735
|
+
error: dirResult.message
|
|
38736
|
+
};
|
|
38737
|
+
return JSON.stringify(errorResult, null, 2);
|
|
38738
|
+
}
|
|
38739
|
+
const workingDir = dirResult.directory;
|
|
38653
38740
|
if (workingDir.length > 4096) {
|
|
38654
38741
|
const errorResult = {
|
|
38655
38742
|
success: false,
|
|
@@ -38703,8 +38790,8 @@ var test_runner = createSwarmTool({
|
|
|
38703
38790
|
success: false,
|
|
38704
38791
|
framework: "none",
|
|
38705
38792
|
scope: "all",
|
|
38706
|
-
error: '
|
|
38707
|
-
message: '
|
|
38793
|
+
error: 'scope "all" is not allowed without explicit files. Use scope "convention" or "graph" with a files array to run targeted tests.',
|
|
38794
|
+
message: 'Running the full test suite without file targeting is blocked. Provide scope "convention" or "graph" with specific source files in the files array. Example: { scope: "convention", files: ["src/tools/test-runner.ts"] }'
|
|
38708
38795
|
};
|
|
38709
38796
|
return JSON.stringify(errorResult, null, 2);
|
|
38710
38797
|
}
|
|
@@ -38744,7 +38831,7 @@ var test_runner = createSwarmTool({
|
|
|
38744
38831
|
let effectiveScope = scope;
|
|
38745
38832
|
if (scope === "all") {} else if (scope === "convention") {
|
|
38746
38833
|
const sourceFiles = args.files.filter((f) => {
|
|
38747
|
-
const ext =
|
|
38834
|
+
const ext = path20.extname(f).toLowerCase();
|
|
38748
38835
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38749
38836
|
});
|
|
38750
38837
|
if (sourceFiles.length === 0) {
|
|
@@ -38760,7 +38847,7 @@ var test_runner = createSwarmTool({
|
|
|
38760
38847
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
38761
38848
|
} else if (scope === "graph") {
|
|
38762
38849
|
const sourceFiles = args.files.filter((f) => {
|
|
38763
|
-
const ext =
|
|
38850
|
+
const ext = path20.extname(f).toLowerCase();
|
|
38764
38851
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38765
38852
|
});
|
|
38766
38853
|
if (sourceFiles.length === 0) {
|
|
@@ -38831,8 +38918,8 @@ function validateDirectoryPath(dir) {
|
|
|
38831
38918
|
if (dir.includes("..")) {
|
|
38832
38919
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
38833
38920
|
}
|
|
38834
|
-
const normalized =
|
|
38835
|
-
const absolutePath =
|
|
38921
|
+
const normalized = path21.normalize(dir);
|
|
38922
|
+
const absolutePath = path21.isAbsolute(normalized) ? normalized : path21.resolve(normalized);
|
|
38836
38923
|
return absolutePath;
|
|
38837
38924
|
}
|
|
38838
38925
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -38855,9 +38942,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
38855
38942
|
}
|
|
38856
38943
|
function getPackageVersion(dir) {
|
|
38857
38944
|
try {
|
|
38858
|
-
const packagePath =
|
|
38859
|
-
if (
|
|
38860
|
-
const content =
|
|
38945
|
+
const packagePath = path21.join(dir, "package.json");
|
|
38946
|
+
if (fs11.existsSync(packagePath)) {
|
|
38947
|
+
const content = fs11.readFileSync(packagePath, "utf-8");
|
|
38861
38948
|
const pkg = JSON.parse(content);
|
|
38862
38949
|
return pkg.version ?? null;
|
|
38863
38950
|
}
|
|
@@ -38866,9 +38953,9 @@ function getPackageVersion(dir) {
|
|
|
38866
38953
|
}
|
|
38867
38954
|
function getChangelogVersion(dir) {
|
|
38868
38955
|
try {
|
|
38869
|
-
const changelogPath =
|
|
38870
|
-
if (
|
|
38871
|
-
const content =
|
|
38956
|
+
const changelogPath = path21.join(dir, "CHANGELOG.md");
|
|
38957
|
+
if (fs11.existsSync(changelogPath)) {
|
|
38958
|
+
const content = fs11.readFileSync(changelogPath, "utf-8");
|
|
38872
38959
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
38873
38960
|
if (match) {
|
|
38874
38961
|
return match[1];
|
|
@@ -38880,10 +38967,10 @@ function getChangelogVersion(dir) {
|
|
|
38880
38967
|
function getVersionFileVersion(dir) {
|
|
38881
38968
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
38882
38969
|
for (const file3 of possibleFiles) {
|
|
38883
|
-
const filePath =
|
|
38884
|
-
if (
|
|
38970
|
+
const filePath = path21.join(dir, file3);
|
|
38971
|
+
if (fs11.existsSync(filePath)) {
|
|
38885
38972
|
try {
|
|
38886
|
-
const content =
|
|
38973
|
+
const content = fs11.readFileSync(filePath, "utf-8").trim();
|
|
38887
38974
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
38888
38975
|
if (match) {
|
|
38889
38976
|
return match[1];
|
|
@@ -39427,7 +39514,7 @@ async function handlePromoteCommand(directory, args) {
|
|
|
39427
39514
|
}
|
|
39428
39515
|
|
|
39429
39516
|
// src/commands/reset.ts
|
|
39430
|
-
import * as
|
|
39517
|
+
import * as fs12 from "fs";
|
|
39431
39518
|
|
|
39432
39519
|
// src/background/manager.ts
|
|
39433
39520
|
init_utils();
|
|
@@ -39482,13 +39569,13 @@ class CircuitBreaker {
|
|
|
39482
39569
|
if (this.config.callTimeoutMs <= 0) {
|
|
39483
39570
|
return fn();
|
|
39484
39571
|
}
|
|
39485
|
-
return new Promise((
|
|
39572
|
+
return new Promise((resolve9, reject) => {
|
|
39486
39573
|
const timeout = setTimeout(() => {
|
|
39487
39574
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
39488
39575
|
}, this.config.callTimeoutMs);
|
|
39489
39576
|
fn().then((result) => {
|
|
39490
39577
|
clearTimeout(timeout);
|
|
39491
|
-
|
|
39578
|
+
resolve9(result);
|
|
39492
39579
|
}).catch((error93) => {
|
|
39493
39580
|
clearTimeout(timeout);
|
|
39494
39581
|
reject(error93);
|
|
@@ -39772,7 +39859,7 @@ class AutomationQueue {
|
|
|
39772
39859
|
|
|
39773
39860
|
// src/background/worker.ts
|
|
39774
39861
|
function sleep(ms) {
|
|
39775
|
-
return new Promise((
|
|
39862
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
39776
39863
|
}
|
|
39777
39864
|
|
|
39778
39865
|
class WorkerManager {
|
|
@@ -40128,8 +40215,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40128
40215
|
for (const filename of filesToReset) {
|
|
40129
40216
|
try {
|
|
40130
40217
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
40131
|
-
if (
|
|
40132
|
-
|
|
40218
|
+
if (fs12.existsSync(resolvedPath)) {
|
|
40219
|
+
fs12.unlinkSync(resolvedPath);
|
|
40133
40220
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
40134
40221
|
} else {
|
|
40135
40222
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -40146,8 +40233,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40146
40233
|
}
|
|
40147
40234
|
try {
|
|
40148
40235
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
40149
|
-
if (
|
|
40150
|
-
|
|
40236
|
+
if (fs12.existsSync(summariesPath)) {
|
|
40237
|
+
fs12.rmSync(summariesPath, { recursive: true, force: true });
|
|
40151
40238
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
40152
40239
|
} else {
|
|
40153
40240
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -40167,14 +40254,14 @@ async function handleResetCommand(directory, args) {
|
|
|
40167
40254
|
|
|
40168
40255
|
// src/commands/reset-session.ts
|
|
40169
40256
|
init_utils2();
|
|
40170
|
-
import * as
|
|
40171
|
-
import * as
|
|
40257
|
+
import * as fs13 from "fs";
|
|
40258
|
+
import * as path22 from "path";
|
|
40172
40259
|
async function handleResetSessionCommand(directory, _args) {
|
|
40173
40260
|
const results = [];
|
|
40174
40261
|
try {
|
|
40175
40262
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
40176
|
-
if (
|
|
40177
|
-
|
|
40263
|
+
if (fs13.existsSync(statePath)) {
|
|
40264
|
+
fs13.unlinkSync(statePath);
|
|
40178
40265
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
40179
40266
|
} else {
|
|
40180
40267
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -40183,15 +40270,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40183
40270
|
results.push("\u274C Failed to delete state.json");
|
|
40184
40271
|
}
|
|
40185
40272
|
try {
|
|
40186
|
-
const sessionDir =
|
|
40187
|
-
if (
|
|
40188
|
-
const files =
|
|
40273
|
+
const sessionDir = path22.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
40274
|
+
if (fs13.existsSync(sessionDir)) {
|
|
40275
|
+
const files = fs13.readdirSync(sessionDir);
|
|
40189
40276
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
40190
40277
|
let deletedCount = 0;
|
|
40191
40278
|
for (const file3 of otherFiles) {
|
|
40192
|
-
const filePath =
|
|
40193
|
-
if (
|
|
40194
|
-
|
|
40279
|
+
const filePath = path22.join(sessionDir, file3);
|
|
40280
|
+
if (fs13.lstatSync(filePath).isFile()) {
|
|
40281
|
+
fs13.unlinkSync(filePath);
|
|
40195
40282
|
deletedCount++;
|
|
40196
40283
|
}
|
|
40197
40284
|
}
|
|
@@ -40219,7 +40306,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40219
40306
|
// src/summaries/manager.ts
|
|
40220
40307
|
init_utils2();
|
|
40221
40308
|
init_utils();
|
|
40222
|
-
import * as
|
|
40309
|
+
import * as path23 from "path";
|
|
40223
40310
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
40224
40311
|
function sanitizeSummaryId(id) {
|
|
40225
40312
|
if (!id || id.length === 0) {
|
|
@@ -40243,7 +40330,7 @@ function sanitizeSummaryId(id) {
|
|
|
40243
40330
|
}
|
|
40244
40331
|
async function loadFullOutput(directory, id) {
|
|
40245
40332
|
const sanitizedId = sanitizeSummaryId(id);
|
|
40246
|
-
const relativePath =
|
|
40333
|
+
const relativePath = path23.join("summaries", `${sanitizedId}.json`);
|
|
40247
40334
|
validateSwarmPath(directory, relativePath);
|
|
40248
40335
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
40249
40336
|
if (content === null) {
|
|
@@ -40296,18 +40383,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
40296
40383
|
|
|
40297
40384
|
// src/commands/rollback.ts
|
|
40298
40385
|
init_utils2();
|
|
40299
|
-
import * as
|
|
40300
|
-
import * as
|
|
40386
|
+
import * as fs14 from "fs";
|
|
40387
|
+
import * as path24 from "path";
|
|
40301
40388
|
async function handleRollbackCommand(directory, args) {
|
|
40302
40389
|
const phaseArg = args[0];
|
|
40303
40390
|
if (!phaseArg) {
|
|
40304
40391
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40305
|
-
if (!
|
|
40392
|
+
if (!fs14.existsSync(manifestPath2)) {
|
|
40306
40393
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
40307
40394
|
}
|
|
40308
40395
|
let manifest2;
|
|
40309
40396
|
try {
|
|
40310
|
-
manifest2 = JSON.parse(
|
|
40397
|
+
manifest2 = JSON.parse(fs14.readFileSync(manifestPath2, "utf-8"));
|
|
40311
40398
|
} catch {
|
|
40312
40399
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
40313
40400
|
}
|
|
@@ -40329,12 +40416,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40329
40416
|
return "Error: Phase number must be a positive integer.";
|
|
40330
40417
|
}
|
|
40331
40418
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40332
|
-
if (!
|
|
40419
|
+
if (!fs14.existsSync(manifestPath)) {
|
|
40333
40420
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
40334
40421
|
}
|
|
40335
40422
|
let manifest;
|
|
40336
40423
|
try {
|
|
40337
|
-
manifest = JSON.parse(
|
|
40424
|
+
manifest = JSON.parse(fs14.readFileSync(manifestPath, "utf-8"));
|
|
40338
40425
|
} catch {
|
|
40339
40426
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
40340
40427
|
}
|
|
@@ -40344,10 +40431,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40344
40431
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
40345
40432
|
}
|
|
40346
40433
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
40347
|
-
if (!
|
|
40434
|
+
if (!fs14.existsSync(checkpointDir)) {
|
|
40348
40435
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
40349
40436
|
}
|
|
40350
|
-
const checkpointFiles =
|
|
40437
|
+
const checkpointFiles = fs14.readdirSync(checkpointDir);
|
|
40351
40438
|
if (checkpointFiles.length === 0) {
|
|
40352
40439
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
40353
40440
|
}
|
|
@@ -40355,10 +40442,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40355
40442
|
const successes = [];
|
|
40356
40443
|
const failures = [];
|
|
40357
40444
|
for (const file3 of checkpointFiles) {
|
|
40358
|
-
const src =
|
|
40359
|
-
const dest =
|
|
40445
|
+
const src = path24.join(checkpointDir, file3);
|
|
40446
|
+
const dest = path24.join(swarmDir, file3);
|
|
40360
40447
|
try {
|
|
40361
|
-
|
|
40448
|
+
fs14.cpSync(src, dest, { recursive: true, force: true });
|
|
40362
40449
|
successes.push(file3);
|
|
40363
40450
|
} catch (error93) {
|
|
40364
40451
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -40375,7 +40462,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40375
40462
|
timestamp: new Date().toISOString()
|
|
40376
40463
|
};
|
|
40377
40464
|
try {
|
|
40378
|
-
|
|
40465
|
+
fs14.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
40379
40466
|
`);
|
|
40380
40467
|
} catch (error93) {
|
|
40381
40468
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -40418,11 +40505,11 @@ async function handleSimulateCommand(directory, args) {
|
|
|
40418
40505
|
];
|
|
40419
40506
|
const report = reportLines.filter(Boolean).join(`
|
|
40420
40507
|
`);
|
|
40421
|
-
const
|
|
40422
|
-
const
|
|
40423
|
-
const reportPath =
|
|
40424
|
-
await
|
|
40425
|
-
await
|
|
40508
|
+
const fs15 = await import("fs/promises");
|
|
40509
|
+
const path25 = await import("path");
|
|
40510
|
+
const reportPath = path25.join(directory, ".swarm", "simulate-report.md");
|
|
40511
|
+
await fs15.mkdir(path25.dirname(reportPath), { recursive: true });
|
|
40512
|
+
await fs15.writeFile(reportPath, report, "utf-8");
|
|
40426
40513
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
40427
40514
|
}
|
|
40428
40515
|
|
|
@@ -40904,18 +40991,18 @@ function resolveCommand(tokens) {
|
|
|
40904
40991
|
}
|
|
40905
40992
|
|
|
40906
40993
|
// src/cli/index.ts
|
|
40907
|
-
var CONFIG_DIR =
|
|
40908
|
-
var OPENCODE_CONFIG_PATH =
|
|
40909
|
-
var PLUGIN_CONFIG_PATH =
|
|
40910
|
-
var PROMPTS_DIR =
|
|
40994
|
+
var CONFIG_DIR = path25.join(process.env.XDG_CONFIG_HOME || path25.join(os5.homedir(), ".config"), "opencode");
|
|
40995
|
+
var OPENCODE_CONFIG_PATH = path25.join(CONFIG_DIR, "opencode.json");
|
|
40996
|
+
var PLUGIN_CONFIG_PATH = path25.join(CONFIG_DIR, "opencode-swarm.json");
|
|
40997
|
+
var PROMPTS_DIR = path25.join(CONFIG_DIR, "opencode-swarm");
|
|
40911
40998
|
function ensureDir(dir) {
|
|
40912
|
-
if (!
|
|
40913
|
-
|
|
40999
|
+
if (!fs15.existsSync(dir)) {
|
|
41000
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
40914
41001
|
}
|
|
40915
41002
|
}
|
|
40916
41003
|
function loadJson(filepath) {
|
|
40917
41004
|
try {
|
|
40918
|
-
const content =
|
|
41005
|
+
const content = fs15.readFileSync(filepath, "utf-8");
|
|
40919
41006
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
40920
41007
|
return JSON.parse(stripped);
|
|
40921
41008
|
} catch {
|
|
@@ -40923,7 +41010,7 @@ function loadJson(filepath) {
|
|
|
40923
41010
|
}
|
|
40924
41011
|
}
|
|
40925
41012
|
function saveJson(filepath, data) {
|
|
40926
|
-
|
|
41013
|
+
fs15.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
40927
41014
|
`, "utf-8");
|
|
40928
41015
|
}
|
|
40929
41016
|
async function install() {
|
|
@@ -40931,7 +41018,7 @@ async function install() {
|
|
|
40931
41018
|
`);
|
|
40932
41019
|
ensureDir(CONFIG_DIR);
|
|
40933
41020
|
ensureDir(PROMPTS_DIR);
|
|
40934
|
-
const LEGACY_CONFIG_PATH =
|
|
41021
|
+
const LEGACY_CONFIG_PATH = path25.join(CONFIG_DIR, "config.json");
|
|
40935
41022
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
40936
41023
|
if (!opencodeConfig) {
|
|
40937
41024
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -40956,7 +41043,7 @@ async function install() {
|
|
|
40956
41043
|
saveJson(OPENCODE_CONFIG_PATH, opencodeConfig);
|
|
40957
41044
|
console.log("\u2713 Added opencode-swarm to OpenCode plugins");
|
|
40958
41045
|
console.log("\u2713 Disabled default OpenCode agents (explore, general)");
|
|
40959
|
-
if (!
|
|
41046
|
+
if (!fs15.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
40960
41047
|
const defaultConfig = {
|
|
40961
41048
|
agents: {
|
|
40962
41049
|
coder: { model: "opencode/minimax-m2.5-free" },
|
|
@@ -40999,7 +41086,7 @@ async function uninstall() {
|
|
|
40999
41086
|
`);
|
|
41000
41087
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41001
41088
|
if (!opencodeConfig) {
|
|
41002
|
-
if (
|
|
41089
|
+
if (fs15.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
41003
41090
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
41004
41091
|
return 1;
|
|
41005
41092
|
} else {
|
|
@@ -41031,13 +41118,13 @@ async function uninstall() {
|
|
|
41031
41118
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
41032
41119
|
if (process.argv.includes("--clean")) {
|
|
41033
41120
|
let cleaned = false;
|
|
41034
|
-
if (
|
|
41035
|
-
|
|
41121
|
+
if (fs15.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
41122
|
+
fs15.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
41036
41123
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
41037
41124
|
cleaned = true;
|
|
41038
41125
|
}
|
|
41039
|
-
if (
|
|
41040
|
-
|
|
41126
|
+
if (fs15.existsSync(PROMPTS_DIR)) {
|
|
41127
|
+
fs15.rmSync(PROMPTS_DIR, { recursive: true });
|
|
41041
41128
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
41042
41129
|
cleaned = true;
|
|
41043
41130
|
}
|