opencode-swarm 6.45.1 → 6.47.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/README.md +121 -0
- package/dist/cli/index.js +353 -240
- package/dist/commands/close.d.ts +1 -1
- package/dist/config/evidence-schema.d.ts +2 -2
- package/dist/config/schema.d.ts +50 -0
- package/dist/hooks/guardrails.d.ts +12 -3
- package/dist/hooks/index.d.ts +1 -1
- package/dist/index.js +765 -587
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -16987,13 +16987,13 @@ __export(exports_config_doctor, {
|
|
|
16987
16987
|
import * as crypto3 from "crypto";
|
|
16988
16988
|
import * as fs7 from "fs";
|
|
16989
16989
|
import * as os4 from "os";
|
|
16990
|
-
import * as
|
|
16990
|
+
import * as path16 from "path";
|
|
16991
16991
|
function getUserConfigDir3() {
|
|
16992
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
16992
|
+
return process.env.XDG_CONFIG_HOME || path16.join(os4.homedir(), ".config");
|
|
16993
16993
|
}
|
|
16994
16994
|
function getConfigPaths(directory) {
|
|
16995
|
-
const userConfigPath =
|
|
16996
|
-
const projectConfigPath =
|
|
16995
|
+
const userConfigPath = path16.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
|
|
16996
|
+
const projectConfigPath = path16.join(directory, ".opencode", "opencode-swarm.json");
|
|
16997
16997
|
return { userConfigPath, projectConfigPath };
|
|
16998
16998
|
}
|
|
16999
16999
|
function computeHash(content) {
|
|
@@ -17018,9 +17018,9 @@ function isValidConfigPath(configPath, directory) {
|
|
|
17018
17018
|
const normalizedUser = userConfigPath.replace(/\\/g, "/");
|
|
17019
17019
|
const normalizedProject = projectConfigPath.replace(/\\/g, "/");
|
|
17020
17020
|
try {
|
|
17021
|
-
const resolvedConfig =
|
|
17022
|
-
const resolvedUser =
|
|
17023
|
-
const resolvedProject =
|
|
17021
|
+
const resolvedConfig = path16.resolve(configPath);
|
|
17022
|
+
const resolvedUser = path16.resolve(normalizedUser);
|
|
17023
|
+
const resolvedProject = path16.resolve(normalizedProject);
|
|
17024
17024
|
return resolvedConfig === resolvedUser || resolvedConfig === resolvedProject;
|
|
17025
17025
|
} catch {
|
|
17026
17026
|
return false;
|
|
@@ -17060,12 +17060,12 @@ function createConfigBackup(directory) {
|
|
|
17060
17060
|
};
|
|
17061
17061
|
}
|
|
17062
17062
|
function writeBackupArtifact(directory, backup) {
|
|
17063
|
-
const swarmDir =
|
|
17063
|
+
const swarmDir = path16.join(directory, ".swarm");
|
|
17064
17064
|
if (!fs7.existsSync(swarmDir)) {
|
|
17065
17065
|
fs7.mkdirSync(swarmDir, { recursive: true });
|
|
17066
17066
|
}
|
|
17067
17067
|
const backupFilename = `config-backup-${backup.createdAt}.json`;
|
|
17068
|
-
const backupPath =
|
|
17068
|
+
const backupPath = path16.join(swarmDir, backupFilename);
|
|
17069
17069
|
const artifact = {
|
|
17070
17070
|
createdAt: backup.createdAt,
|
|
17071
17071
|
configPath: backup.configPath,
|
|
@@ -17095,7 +17095,7 @@ function restoreFromBackup(backupPath, directory) {
|
|
|
17095
17095
|
return null;
|
|
17096
17096
|
}
|
|
17097
17097
|
const targetPath = artifact.configPath;
|
|
17098
|
-
const targetDir =
|
|
17098
|
+
const targetDir = path16.dirname(targetPath);
|
|
17099
17099
|
if (!fs7.existsSync(targetDir)) {
|
|
17100
17100
|
fs7.mkdirSync(targetDir, { recursive: true });
|
|
17101
17101
|
}
|
|
@@ -17126,9 +17126,9 @@ function readConfigFromFile(directory) {
|
|
|
17126
17126
|
return null;
|
|
17127
17127
|
}
|
|
17128
17128
|
}
|
|
17129
|
-
function validateConfigKey(
|
|
17129
|
+
function validateConfigKey(path17, value, _config) {
|
|
17130
17130
|
const findings = [];
|
|
17131
|
-
switch (
|
|
17131
|
+
switch (path17) {
|
|
17132
17132
|
case "agents": {
|
|
17133
17133
|
if (value !== undefined) {
|
|
17134
17134
|
findings.push({
|
|
@@ -17375,27 +17375,27 @@ function validateConfigKey(path16, value, _config) {
|
|
|
17375
17375
|
}
|
|
17376
17376
|
return findings;
|
|
17377
17377
|
}
|
|
17378
|
-
function walkConfigAndValidate(obj,
|
|
17378
|
+
function walkConfigAndValidate(obj, path17, config3, findings) {
|
|
17379
17379
|
if (obj === null || obj === undefined) {
|
|
17380
17380
|
return;
|
|
17381
17381
|
}
|
|
17382
|
-
if (
|
|
17383
|
-
const keyFindings = validateConfigKey(
|
|
17382
|
+
if (path17 && typeof obj === "object" && !Array.isArray(obj)) {
|
|
17383
|
+
const keyFindings = validateConfigKey(path17, obj, config3);
|
|
17384
17384
|
findings.push(...keyFindings);
|
|
17385
17385
|
}
|
|
17386
17386
|
if (typeof obj !== "object") {
|
|
17387
|
-
const keyFindings = validateConfigKey(
|
|
17387
|
+
const keyFindings = validateConfigKey(path17, obj, config3);
|
|
17388
17388
|
findings.push(...keyFindings);
|
|
17389
17389
|
return;
|
|
17390
17390
|
}
|
|
17391
17391
|
if (Array.isArray(obj)) {
|
|
17392
17392
|
obj.forEach((item, index) => {
|
|
17393
|
-
walkConfigAndValidate(item, `${
|
|
17393
|
+
walkConfigAndValidate(item, `${path17}[${index}]`, config3, findings);
|
|
17394
17394
|
});
|
|
17395
17395
|
return;
|
|
17396
17396
|
}
|
|
17397
17397
|
for (const [key, value] of Object.entries(obj)) {
|
|
17398
|
-
const newPath =
|
|
17398
|
+
const newPath = path17 ? `${path17}.${key}` : key;
|
|
17399
17399
|
walkConfigAndValidate(value, newPath, config3, findings);
|
|
17400
17400
|
}
|
|
17401
17401
|
}
|
|
@@ -17515,7 +17515,7 @@ function applySafeAutoFixes(directory, result) {
|
|
|
17515
17515
|
}
|
|
17516
17516
|
}
|
|
17517
17517
|
if (appliedFixes.length > 0) {
|
|
17518
|
-
const configDir =
|
|
17518
|
+
const configDir = path16.dirname(configPath);
|
|
17519
17519
|
if (!fs7.existsSync(configDir)) {
|
|
17520
17520
|
fs7.mkdirSync(configDir, { recursive: true });
|
|
17521
17521
|
}
|
|
@@ -17525,12 +17525,12 @@ function applySafeAutoFixes(directory, result) {
|
|
|
17525
17525
|
return { appliedFixes, updatedConfigPath };
|
|
17526
17526
|
}
|
|
17527
17527
|
function writeDoctorArtifact(directory, result) {
|
|
17528
|
-
const swarmDir =
|
|
17528
|
+
const swarmDir = path16.join(directory, ".swarm");
|
|
17529
17529
|
if (!fs7.existsSync(swarmDir)) {
|
|
17530
17530
|
fs7.mkdirSync(swarmDir, { recursive: true });
|
|
17531
17531
|
}
|
|
17532
17532
|
const artifactFilename = "config-doctor.json";
|
|
17533
|
-
const artifactPath =
|
|
17533
|
+
const artifactPath = path16.join(swarmDir, artifactFilename);
|
|
17534
17534
|
const guiOutput = {
|
|
17535
17535
|
timestamp: result.timestamp,
|
|
17536
17536
|
summary: result.summary,
|
|
@@ -17893,7 +17893,7 @@ var init_evidence_summary_service = __esm(() => {
|
|
|
17893
17893
|
// src/cli/index.ts
|
|
17894
17894
|
import * as fs18 from "fs";
|
|
17895
17895
|
import * as os6 from "os";
|
|
17896
|
-
import * as
|
|
17896
|
+
import * as path28 from "path";
|
|
17897
17897
|
|
|
17898
17898
|
// src/commands/agents.ts
|
|
17899
17899
|
function handleAgentsCommand(agents, guardrails) {
|
|
@@ -18649,6 +18649,17 @@ var CompactionConfigSchema = exports_external.object({
|
|
|
18649
18649
|
emergencyThreshold: exports_external.number().min(1).max(99).default(80),
|
|
18650
18650
|
preserveLastNTurns: exports_external.number().int().min(1).default(5)
|
|
18651
18651
|
});
|
|
18652
|
+
var AgentAuthorityRuleSchema = exports_external.object({
|
|
18653
|
+
readOnly: exports_external.boolean().optional(),
|
|
18654
|
+
blockedExact: exports_external.array(exports_external.string()).optional(),
|
|
18655
|
+
blockedPrefix: exports_external.array(exports_external.string()).optional(),
|
|
18656
|
+
allowedPrefix: exports_external.array(exports_external.string()).optional(),
|
|
18657
|
+
blockedZones: exports_external.array(exports_external.enum(["production", "test", "config", "generated", "docs", "build"])).optional()
|
|
18658
|
+
});
|
|
18659
|
+
var AuthorityConfigSchema = exports_external.object({
|
|
18660
|
+
enabled: exports_external.boolean().default(true),
|
|
18661
|
+
rules: exports_external.record(exports_external.string(), AgentAuthorityRuleSchema).default({})
|
|
18662
|
+
});
|
|
18652
18663
|
var PluginConfigSchema = exports_external.object({
|
|
18653
18664
|
agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
|
|
18654
18665
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
@@ -18665,6 +18676,7 @@ var PluginConfigSchema = exports_external.object({
|
|
|
18665
18676
|
watchdog: WatchdogConfigSchema.optional(),
|
|
18666
18677
|
self_review: SelfReviewConfigSchema.optional(),
|
|
18667
18678
|
tool_filter: ToolFilterConfigSchema.optional(),
|
|
18679
|
+
authority: AuthorityConfigSchema.optional(),
|
|
18668
18680
|
plan_cursor: PlanCursorConfigSchema.optional(),
|
|
18669
18681
|
evidence: EvidenceConfigSchema.optional(),
|
|
18670
18682
|
summaries: SummaryConfigSchema.optional(),
|
|
@@ -31993,7 +32005,9 @@ async function handleClarifyCommand(_directory, args) {
|
|
|
31993
32005
|
}
|
|
31994
32006
|
|
|
31995
32007
|
// src/commands/close.ts
|
|
32008
|
+
import { execFileSync } from "child_process";
|
|
31996
32009
|
import { promises as fs6 } from "fs";
|
|
32010
|
+
import path11 from "path";
|
|
31997
32011
|
init_manager();
|
|
31998
32012
|
|
|
31999
32013
|
// src/hooks/knowledge-store.ts
|
|
@@ -33105,87 +33119,183 @@ var write_retro = createSwarmTool({
|
|
|
33105
33119
|
});
|
|
33106
33120
|
|
|
33107
33121
|
// src/commands/close.ts
|
|
33108
|
-
async function handleCloseCommand(directory,
|
|
33122
|
+
async function handleCloseCommand(directory, args) {
|
|
33109
33123
|
const planPath = validateSwarmPath(directory, "plan.json");
|
|
33110
|
-
let
|
|
33124
|
+
let planExists = false;
|
|
33125
|
+
let planData = {
|
|
33126
|
+
title: path11.basename(directory) || "Ad-hoc session",
|
|
33127
|
+
phases: []
|
|
33128
|
+
};
|
|
33111
33129
|
try {
|
|
33112
33130
|
const content = await fs6.readFile(planPath, "utf-8");
|
|
33113
33131
|
planData = JSON.parse(content);
|
|
33132
|
+
planExists = true;
|
|
33114
33133
|
} catch (error93) {
|
|
33115
|
-
|
|
33134
|
+
if (error93?.code !== "ENOENT") {
|
|
33135
|
+
return `\u274C Failed to read plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
33136
|
+
}
|
|
33137
|
+
const swarmDirExists = await fs6.access(path11.join(directory, ".swarm")).then(() => true).catch(() => false);
|
|
33138
|
+
if (!swarmDirExists) {
|
|
33139
|
+
return `\u274C No .swarm/ directory found in ${directory}. Run /swarm close from the project root, or run /swarm plan first.`;
|
|
33140
|
+
}
|
|
33116
33141
|
}
|
|
33117
33142
|
const phases = planData.phases ?? [];
|
|
33118
33143
|
const inProgressPhases = phases.filter((p) => p.status === "in_progress");
|
|
33119
|
-
|
|
33120
|
-
if (
|
|
33121
|
-
|
|
33122
|
-
const blockedCount = phases.filter((p) => p.status === "blocked").length;
|
|
33123
|
-
const completeCount = phases.filter((p) => p.status === "complete" || p.status === "completed").length;
|
|
33124
|
-
return `\u2139\uFE0F Swarm already closed. ${completeCount} phases complete, ${closedCount} phases closed, ${blockedCount} phases blocked. No action taken.`;
|
|
33144
|
+
let planAlreadyDone = false;
|
|
33145
|
+
if (planExists) {
|
|
33146
|
+
planAlreadyDone = phases.length > 0 && phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
|
|
33125
33147
|
}
|
|
33126
33148
|
const config3 = KnowledgeConfigSchema.parse({});
|
|
33127
33149
|
const projectName = planData.title ?? "Unknown Project";
|
|
33128
33150
|
const closedPhases = [];
|
|
33129
33151
|
const closedTasks = [];
|
|
33130
33152
|
const warnings = [];
|
|
33131
|
-
|
|
33132
|
-
|
|
33133
|
-
|
|
33134
|
-
|
|
33135
|
-
|
|
33136
|
-
|
|
33137
|
-
|
|
33138
|
-
|
|
33139
|
-
|
|
33140
|
-
|
|
33141
|
-
|
|
33142
|
-
|
|
33143
|
-
|
|
33144
|
-
|
|
33145
|
-
|
|
33146
|
-
|
|
33147
|
-
|
|
33148
|
-
|
|
33153
|
+
if (!planAlreadyDone) {
|
|
33154
|
+
for (const phase of inProgressPhases) {
|
|
33155
|
+
closedPhases.push(phase.id);
|
|
33156
|
+
let retroResult;
|
|
33157
|
+
try {
|
|
33158
|
+
retroResult = await executeWriteRetro({
|
|
33159
|
+
phase: phase.id,
|
|
33160
|
+
summary: "Phase closed via /swarm close",
|
|
33161
|
+
task_count: Math.max(1, (phase.tasks ?? []).length),
|
|
33162
|
+
task_complexity: "simple",
|
|
33163
|
+
total_tool_calls: 0,
|
|
33164
|
+
coder_revisions: 0,
|
|
33165
|
+
reviewer_rejections: 0,
|
|
33166
|
+
test_failures: 0,
|
|
33167
|
+
security_findings: 0,
|
|
33168
|
+
integration_issues: 0
|
|
33169
|
+
}, directory);
|
|
33170
|
+
} catch (retroError) {
|
|
33171
|
+
warnings.push(`Retrospective write threw for phase ${phase.id}: ${retroError instanceof Error ? retroError.message : String(retroError)}`);
|
|
33172
|
+
}
|
|
33173
|
+
if (retroResult !== undefined) {
|
|
33174
|
+
try {
|
|
33175
|
+
const parsed = JSON.parse(retroResult);
|
|
33176
|
+
if (parsed.success !== true) {
|
|
33177
|
+
warnings.push(`Retrospective write failed for phase ${phase.id}`);
|
|
33178
|
+
}
|
|
33179
|
+
} catch {}
|
|
33149
33180
|
}
|
|
33150
|
-
|
|
33151
|
-
|
|
33152
|
-
|
|
33153
|
-
|
|
33181
|
+
for (const task of phase.tasks ?? []) {
|
|
33182
|
+
if (task.status !== "completed" && task.status !== "complete") {
|
|
33183
|
+
closedTasks.push(task.id);
|
|
33184
|
+
}
|
|
33154
33185
|
}
|
|
33155
33186
|
}
|
|
33156
33187
|
}
|
|
33188
|
+
const lessonsFilePath = path11.join(directory, ".swarm", "close-lessons.md");
|
|
33189
|
+
let explicitLessons = [];
|
|
33157
33190
|
try {
|
|
33158
|
-
|
|
33191
|
+
const lessonsText = await fs6.readFile(lessonsFilePath, "utf-8");
|
|
33192
|
+
explicitLessons = lessonsText.split(`
|
|
33193
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
33194
|
+
} catch {}
|
|
33195
|
+
let curationSucceeded = false;
|
|
33196
|
+
try {
|
|
33197
|
+
await curateAndStoreSwarm(explicitLessons, projectName, { phase_number: 0 }, directory, config3);
|
|
33198
|
+
curationSucceeded = true;
|
|
33159
33199
|
} catch (error93) {
|
|
33160
33200
|
console.warn("[close-command] curateAndStoreSwarm error:", error93);
|
|
33161
33201
|
}
|
|
33162
|
-
|
|
33163
|
-
|
|
33164
|
-
|
|
33165
|
-
|
|
33166
|
-
|
|
33202
|
+
if (curationSucceeded && explicitLessons.length > 0) {
|
|
33203
|
+
await fs6.unlink(lessonsFilePath).catch(() => {});
|
|
33204
|
+
}
|
|
33205
|
+
if (planExists && !planAlreadyDone) {
|
|
33206
|
+
for (const phase of phases) {
|
|
33207
|
+
if (phase.status !== "complete" && phase.status !== "completed") {
|
|
33208
|
+
phase.status = "closed";
|
|
33209
|
+
if (!closedPhases.includes(phase.id)) {
|
|
33210
|
+
closedPhases.push(phase.id);
|
|
33211
|
+
}
|
|
33167
33212
|
}
|
|
33168
|
-
|
|
33169
|
-
|
|
33170
|
-
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
|
|
33213
|
+
for (const task of phase.tasks ?? []) {
|
|
33214
|
+
if (task.status !== "completed" && task.status !== "complete") {
|
|
33215
|
+
task.status = "closed";
|
|
33216
|
+
if (!closedTasks.includes(task.id)) {
|
|
33217
|
+
closedTasks.push(task.id);
|
|
33218
|
+
}
|
|
33174
33219
|
}
|
|
33175
33220
|
}
|
|
33176
33221
|
}
|
|
33222
|
+
try {
|
|
33223
|
+
await fs6.writeFile(planPath, JSON.stringify(planData, null, 2), "utf-8");
|
|
33224
|
+
} catch (error93) {
|
|
33225
|
+
console.warn("[close-command] Failed to write plan.json:", error93);
|
|
33226
|
+
}
|
|
33177
33227
|
}
|
|
33178
33228
|
try {
|
|
33179
|
-
await
|
|
33229
|
+
await archiveEvidence(directory, 30, 10);
|
|
33180
33230
|
} catch (error93) {
|
|
33181
|
-
console.warn("[close-command]
|
|
33231
|
+
console.warn("[close-command] archiveEvidence error:", error93);
|
|
33182
33232
|
}
|
|
33233
|
+
const swarmDir = path11.join(directory, ".swarm");
|
|
33234
|
+
let configBackupsRemoved = 0;
|
|
33183
33235
|
try {
|
|
33184
|
-
await
|
|
33236
|
+
const swarmFiles = await fs6.readdir(swarmDir);
|
|
33237
|
+
const configBackups = swarmFiles.filter((f) => f.startsWith("config-backup-") && f.endsWith(".json"));
|
|
33238
|
+
for (const backup of configBackups) {
|
|
33239
|
+
try {
|
|
33240
|
+
await fs6.unlink(path11.join(swarmDir, backup));
|
|
33241
|
+
configBackupsRemoved++;
|
|
33242
|
+
} catch {}
|
|
33243
|
+
}
|
|
33244
|
+
} catch {}
|
|
33245
|
+
const contextPath = path11.join(directory, ".swarm", "context.md");
|
|
33246
|
+
const contextContent = [
|
|
33247
|
+
"# Context",
|
|
33248
|
+
"",
|
|
33249
|
+
"## Status",
|
|
33250
|
+
`Session closed after: ${projectName}`,
|
|
33251
|
+
`Closed: ${new Date().toISOString()}`,
|
|
33252
|
+
"No active plan. Next session starts fresh.",
|
|
33253
|
+
""
|
|
33254
|
+
].join(`
|
|
33255
|
+
`);
|
|
33256
|
+
try {
|
|
33257
|
+
await fs6.writeFile(contextPath, contextContent, "utf-8");
|
|
33185
33258
|
} catch (error93) {
|
|
33186
|
-
console.warn("[close-command]
|
|
33259
|
+
console.warn("[close-command] Failed to write context.md:", error93);
|
|
33260
|
+
}
|
|
33261
|
+
const pruneBranches = args.includes("--prune-branches");
|
|
33262
|
+
const prunedBranches = [];
|
|
33263
|
+
const pruneErrors = [];
|
|
33264
|
+
if (pruneBranches) {
|
|
33265
|
+
try {
|
|
33266
|
+
const branchOutput = execFileSync("git", ["branch", "-vv"], {
|
|
33267
|
+
cwd: directory,
|
|
33268
|
+
encoding: "utf-8",
|
|
33269
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
33270
|
+
});
|
|
33271
|
+
const goneBranches = branchOutput.split(`
|
|
33272
|
+
`).filter((line) => line.includes(": gone]")).map((line) => line.trim().replace(/^[*+]\s+/, "").split(/\s+/)[0]).filter(Boolean);
|
|
33273
|
+
for (const branch of goneBranches) {
|
|
33274
|
+
try {
|
|
33275
|
+
execFileSync("git", ["branch", "-d", branch], {
|
|
33276
|
+
cwd: directory,
|
|
33277
|
+
encoding: "utf-8",
|
|
33278
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
33279
|
+
});
|
|
33280
|
+
prunedBranches.push(branch);
|
|
33281
|
+
} catch {
|
|
33282
|
+
pruneErrors.push(branch);
|
|
33283
|
+
}
|
|
33284
|
+
}
|
|
33285
|
+
} catch {}
|
|
33187
33286
|
}
|
|
33188
33287
|
const closeSummaryPath = validateSwarmPath(directory, "close-summary.md");
|
|
33288
|
+
const actionsPerformed = [
|
|
33289
|
+
...!planAlreadyDone && inProgressPhases.length > 0 ? ["- Wrote retrospectives for in-progress phases"] : [],
|
|
33290
|
+
"- Archived evidence bundles",
|
|
33291
|
+
"- Reset context.md for next session",
|
|
33292
|
+
...configBackupsRemoved > 0 ? [`- Removed ${configBackupsRemoved} stale config backup file(s)`] : [],
|
|
33293
|
+
...prunedBranches.length > 0 ? [
|
|
33294
|
+
`- Pruned ${prunedBranches.length} stale local git branch(es): ${prunedBranches.join(", ")}`
|
|
33295
|
+
] : [],
|
|
33296
|
+
"- Cleared agent sessions and delegation chains",
|
|
33297
|
+
...planExists && !planAlreadyDone ? ["- Set non-completed phases/tasks to closed status"] : []
|
|
33298
|
+
];
|
|
33189
33299
|
const summaryContent = [
|
|
33190
33300
|
"# Swarm Close Summary",
|
|
33191
33301
|
"",
|
|
@@ -33193,18 +33303,15 @@ async function handleCloseCommand(directory, _args) {
|
|
|
33193
33303
|
`**Closed:** ${new Date().toISOString()}`,
|
|
33194
33304
|
"",
|
|
33195
33305
|
`## Phases Closed: ${closedPhases.length}`,
|
|
33196
|
-
closedPhases.map((id) => `- Phase ${id}`).join(`
|
|
33197
|
-
`),
|
|
33306
|
+
!planExists ? "_No plan \u2014 ad-hoc session_" : closedPhases.length > 0 ? closedPhases.map((id) => `- Phase ${id}`).join(`
|
|
33307
|
+
`) : "_No phases to close_",
|
|
33198
33308
|
"",
|
|
33199
33309
|
`## Tasks Closed: ${closedTasks.length}`,
|
|
33200
33310
|
closedTasks.length > 0 ? closedTasks.map((id) => `- ${id}`).join(`
|
|
33201
33311
|
`) : "_No incomplete tasks_",
|
|
33202
33312
|
"",
|
|
33203
33313
|
"## Actions Performed",
|
|
33204
|
-
|
|
33205
|
-
"- Archived evidence bundles",
|
|
33206
|
-
"- Cleared agent sessions and delegation chains",
|
|
33207
|
-
"- Set non-completed phases/tasks to closed status"
|
|
33314
|
+
...actionsPerformed
|
|
33208
33315
|
].join(`
|
|
33209
33316
|
`);
|
|
33210
33317
|
try {
|
|
@@ -33220,20 +33327,26 @@ async function handleCloseCommand(directory, _args) {
|
|
|
33220
33327
|
await writeCheckpoint(directory).catch(() => {});
|
|
33221
33328
|
swarmState.agentSessions.clear();
|
|
33222
33329
|
swarmState.delegationChains.clear();
|
|
33330
|
+
if (pruneErrors.length > 0) {
|
|
33331
|
+
warnings.push(`Could not prune ${pruneErrors.length} branch(es) (unmerged or checked out): ${pruneErrors.join(", ")}`);
|
|
33332
|
+
}
|
|
33223
33333
|
const warningMsg = warnings.length > 0 ? ` Warnings: ${warnings.join("; ")}.` : "";
|
|
33334
|
+
if (planAlreadyDone) {
|
|
33335
|
+
return `\u2705 Session closed. Plan was already in a terminal state \u2014 cleanup steps applied.${warningMsg}`;
|
|
33336
|
+
}
|
|
33224
33337
|
return `\u2705 Swarm closed successfully. ${closedPhases.length} phase(s) closed, ${closedTasks.length} incomplete task(s) marked closed.${warningMsg}`;
|
|
33225
33338
|
}
|
|
33226
33339
|
|
|
33227
33340
|
// src/commands/config.ts
|
|
33228
33341
|
import * as os3 from "os";
|
|
33229
|
-
import * as
|
|
33342
|
+
import * as path12 from "path";
|
|
33230
33343
|
function getUserConfigDir2() {
|
|
33231
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
33344
|
+
return process.env.XDG_CONFIG_HOME || path12.join(os3.homedir(), ".config");
|
|
33232
33345
|
}
|
|
33233
33346
|
async function handleConfigCommand(directory, _args) {
|
|
33234
33347
|
const config3 = loadPluginConfig(directory);
|
|
33235
|
-
const userConfigPath =
|
|
33236
|
-
const projectConfigPath =
|
|
33348
|
+
const userConfigPath = path12.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
|
|
33349
|
+
const projectConfigPath = path12.join(directory, ".opencode", "opencode-swarm.json");
|
|
33237
33350
|
const lines = [
|
|
33238
33351
|
"## Swarm Configuration",
|
|
33239
33352
|
"",
|
|
@@ -33501,13 +33614,13 @@ function formatCurationSummary(summary) {
|
|
|
33501
33614
|
}
|
|
33502
33615
|
|
|
33503
33616
|
// src/commands/dark-matter.ts
|
|
33504
|
-
import
|
|
33617
|
+
import path14 from "path";
|
|
33505
33618
|
|
|
33506
33619
|
// src/tools/co-change-analyzer.ts
|
|
33507
33620
|
import * as child_process2 from "child_process";
|
|
33508
33621
|
import { randomUUID } from "crypto";
|
|
33509
33622
|
import { readdir, readFile as readFile2, stat } from "fs/promises";
|
|
33510
|
-
import * as
|
|
33623
|
+
import * as path13 from "path";
|
|
33511
33624
|
import { promisify } from "util";
|
|
33512
33625
|
function getExecFileAsync() {
|
|
33513
33626
|
return promisify(child_process2.execFile);
|
|
@@ -33609,7 +33722,7 @@ async function scanSourceFiles(dir) {
|
|
|
33609
33722
|
try {
|
|
33610
33723
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
33611
33724
|
for (const entry of entries) {
|
|
33612
|
-
const fullPath =
|
|
33725
|
+
const fullPath = path13.join(dir, entry.name);
|
|
33613
33726
|
if (entry.isDirectory()) {
|
|
33614
33727
|
if (skipDirs.has(entry.name)) {
|
|
33615
33728
|
continue;
|
|
@@ -33617,7 +33730,7 @@ async function scanSourceFiles(dir) {
|
|
|
33617
33730
|
const subFiles = await scanSourceFiles(fullPath);
|
|
33618
33731
|
results.push(...subFiles);
|
|
33619
33732
|
} else if (entry.isFile()) {
|
|
33620
|
-
const ext =
|
|
33733
|
+
const ext = path13.extname(entry.name);
|
|
33621
33734
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
|
|
33622
33735
|
results.push(fullPath);
|
|
33623
33736
|
}
|
|
@@ -33639,8 +33752,8 @@ async function getStaticEdges(directory) {
|
|
|
33639
33752
|
continue;
|
|
33640
33753
|
}
|
|
33641
33754
|
try {
|
|
33642
|
-
const sourceDir =
|
|
33643
|
-
const resolvedPath =
|
|
33755
|
+
const sourceDir = path13.dirname(sourceFile);
|
|
33756
|
+
const resolvedPath = path13.resolve(sourceDir, importPath);
|
|
33644
33757
|
const extensions = [
|
|
33645
33758
|
"",
|
|
33646
33759
|
".ts",
|
|
@@ -33665,8 +33778,8 @@ async function getStaticEdges(directory) {
|
|
|
33665
33778
|
if (!targetFile) {
|
|
33666
33779
|
continue;
|
|
33667
33780
|
}
|
|
33668
|
-
const relSource =
|
|
33669
|
-
const relTarget =
|
|
33781
|
+
const relSource = path13.relative(directory, sourceFile).replace(/\\/g, "/");
|
|
33782
|
+
const relTarget = path13.relative(directory, targetFile).replace(/\\/g, "/");
|
|
33670
33783
|
const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
|
|
33671
33784
|
edges.add(key);
|
|
33672
33785
|
} catch {}
|
|
@@ -33678,7 +33791,7 @@ async function getStaticEdges(directory) {
|
|
|
33678
33791
|
function isTestImplementationPair(fileA, fileB) {
|
|
33679
33792
|
const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
|
|
33680
33793
|
const getBaseName = (filePath) => {
|
|
33681
|
-
const base =
|
|
33794
|
+
const base = path13.basename(filePath);
|
|
33682
33795
|
for (const pattern of testPatterns) {
|
|
33683
33796
|
if (base.endsWith(pattern)) {
|
|
33684
33797
|
return base.slice(0, -pattern.length);
|
|
@@ -33688,16 +33801,16 @@ function isTestImplementationPair(fileA, fileB) {
|
|
|
33688
33801
|
};
|
|
33689
33802
|
const baseA = getBaseName(fileA);
|
|
33690
33803
|
const baseB = getBaseName(fileB);
|
|
33691
|
-
return baseA === baseB && baseA !==
|
|
33804
|
+
return baseA === baseB && baseA !== path13.basename(fileA) && baseA !== path13.basename(fileB);
|
|
33692
33805
|
}
|
|
33693
33806
|
function hasSharedPrefix(fileA, fileB) {
|
|
33694
|
-
const dirA =
|
|
33695
|
-
const dirB =
|
|
33807
|
+
const dirA = path13.dirname(fileA);
|
|
33808
|
+
const dirB = path13.dirname(fileB);
|
|
33696
33809
|
if (dirA !== dirB) {
|
|
33697
33810
|
return false;
|
|
33698
33811
|
}
|
|
33699
|
-
const baseA =
|
|
33700
|
-
const baseB =
|
|
33812
|
+
const baseA = path13.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
33813
|
+
const baseB = path13.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
33701
33814
|
if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
|
|
33702
33815
|
return true;
|
|
33703
33816
|
}
|
|
@@ -33751,8 +33864,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
|
|
|
33751
33864
|
const entries = [];
|
|
33752
33865
|
const now = new Date().toISOString();
|
|
33753
33866
|
for (const pair of pairs.slice(0, 10)) {
|
|
33754
|
-
const baseA =
|
|
33755
|
-
const baseB =
|
|
33867
|
+
const baseA = path13.basename(pair.fileA);
|
|
33868
|
+
const baseB = path13.basename(pair.fileB);
|
|
33756
33869
|
let lesson = `Files ${pair.fileA} and ${pair.fileB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
|
|
33757
33870
|
if (lesson.length > 280) {
|
|
33758
33871
|
lesson = `Files ${baseA} and ${baseB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
|
|
@@ -33862,7 +33975,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33862
33975
|
const output = formatDarkMatterOutput(pairs);
|
|
33863
33976
|
if (pairs.length > 0) {
|
|
33864
33977
|
try {
|
|
33865
|
-
const projectName =
|
|
33978
|
+
const projectName = path14.basename(path14.resolve(directory));
|
|
33866
33979
|
const entries = darkMatterToKnowledgeEntries(pairs, projectName);
|
|
33867
33980
|
if (entries.length > 0) {
|
|
33868
33981
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
@@ -33884,7 +33997,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33884
33997
|
// src/services/diagnose-service.ts
|
|
33885
33998
|
import * as child_process3 from "child_process";
|
|
33886
33999
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
33887
|
-
import
|
|
34000
|
+
import path15 from "path";
|
|
33888
34001
|
import { fileURLToPath } from "url";
|
|
33889
34002
|
init_manager();
|
|
33890
34003
|
init_utils2();
|
|
@@ -34184,7 +34297,7 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
34184
34297
|
};
|
|
34185
34298
|
}
|
|
34186
34299
|
async function checkConfigParseability(directory) {
|
|
34187
|
-
const configPath =
|
|
34300
|
+
const configPath = path15.join(directory, ".opencode/opencode-swarm.json");
|
|
34188
34301
|
if (!existsSync5(configPath)) {
|
|
34189
34302
|
return {
|
|
34190
34303
|
name: "Config Parseability",
|
|
@@ -34231,15 +34344,15 @@ async function checkGrammarWasmFiles() {
|
|
|
34231
34344
|
"tree-sitter-ini.wasm",
|
|
34232
34345
|
"tree-sitter-regex.wasm"
|
|
34233
34346
|
];
|
|
34234
|
-
const thisDir =
|
|
34347
|
+
const thisDir = path15.dirname(fileURLToPath(import.meta.url));
|
|
34235
34348
|
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
34236
|
-
const grammarDir = isSource ?
|
|
34349
|
+
const grammarDir = isSource ? path15.join(thisDir, "..", "lang", "grammars") : path15.join(thisDir, "lang", "grammars");
|
|
34237
34350
|
const missing = [];
|
|
34238
|
-
if (!existsSync5(
|
|
34351
|
+
if (!existsSync5(path15.join(grammarDir, "tree-sitter.wasm"))) {
|
|
34239
34352
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
34240
34353
|
}
|
|
34241
34354
|
for (const file3 of grammarFiles) {
|
|
34242
|
-
if (!existsSync5(
|
|
34355
|
+
if (!existsSync5(path15.join(grammarDir, file3))) {
|
|
34243
34356
|
missing.push(file3);
|
|
34244
34357
|
}
|
|
34245
34358
|
}
|
|
@@ -34257,7 +34370,7 @@ async function checkGrammarWasmFiles() {
|
|
|
34257
34370
|
};
|
|
34258
34371
|
}
|
|
34259
34372
|
async function checkCheckpointManifest(directory) {
|
|
34260
|
-
const manifestPath =
|
|
34373
|
+
const manifestPath = path15.join(directory, ".swarm/checkpoints.json");
|
|
34261
34374
|
if (!existsSync5(manifestPath)) {
|
|
34262
34375
|
return {
|
|
34263
34376
|
name: "Checkpoint Manifest",
|
|
@@ -34309,7 +34422,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
34309
34422
|
}
|
|
34310
34423
|
}
|
|
34311
34424
|
async function checkEventStreamIntegrity(directory) {
|
|
34312
|
-
const eventsPath =
|
|
34425
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34313
34426
|
if (!existsSync5(eventsPath)) {
|
|
34314
34427
|
return {
|
|
34315
34428
|
name: "Event Stream",
|
|
@@ -34350,7 +34463,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
34350
34463
|
}
|
|
34351
34464
|
}
|
|
34352
34465
|
async function checkSteeringDirectives(directory) {
|
|
34353
|
-
const eventsPath =
|
|
34466
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34354
34467
|
if (!existsSync5(eventsPath)) {
|
|
34355
34468
|
return {
|
|
34356
34469
|
name: "Steering Directives",
|
|
@@ -34406,7 +34519,7 @@ async function checkCurator(directory) {
|
|
|
34406
34519
|
detail: "Disabled (enable via curator.enabled)"
|
|
34407
34520
|
};
|
|
34408
34521
|
}
|
|
34409
|
-
const summaryPath =
|
|
34522
|
+
const summaryPath = path15.join(directory, ".swarm/curator-summary.json");
|
|
34410
34523
|
if (!existsSync5(summaryPath)) {
|
|
34411
34524
|
return {
|
|
34412
34525
|
name: "Curator",
|
|
@@ -35303,10 +35416,10 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
35303
35416
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
35304
35417
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
35305
35418
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
35306
|
-
import * as
|
|
35419
|
+
import * as path17 from "path";
|
|
35307
35420
|
async function migrateContextToKnowledge(directory, config3) {
|
|
35308
|
-
const sentinelPath =
|
|
35309
|
-
const contextPath =
|
|
35421
|
+
const sentinelPath = path17.join(directory, ".swarm", ".knowledge-migrated");
|
|
35422
|
+
const contextPath = path17.join(directory, ".swarm", "context.md");
|
|
35310
35423
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
35311
35424
|
if (existsSync7(sentinelPath)) {
|
|
35312
35425
|
return {
|
|
@@ -35502,7 +35615,7 @@ function truncateLesson(text) {
|
|
|
35502
35615
|
return `${text.slice(0, 277)}...`;
|
|
35503
35616
|
}
|
|
35504
35617
|
function inferProjectName(directory) {
|
|
35505
|
-
const packageJsonPath =
|
|
35618
|
+
const packageJsonPath = path17.join(directory, "package.json");
|
|
35506
35619
|
if (existsSync7(packageJsonPath)) {
|
|
35507
35620
|
try {
|
|
35508
35621
|
const pkg = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
|
|
@@ -35511,7 +35624,7 @@ function inferProjectName(directory) {
|
|
|
35511
35624
|
}
|
|
35512
35625
|
} catch {}
|
|
35513
35626
|
}
|
|
35514
|
-
return
|
|
35627
|
+
return path17.basename(directory);
|
|
35515
35628
|
}
|
|
35516
35629
|
async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
35517
35630
|
const sentinel = {
|
|
@@ -35523,7 +35636,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
35523
35636
|
schema_version: 1,
|
|
35524
35637
|
migration_tool: "knowledge-migrator.ts"
|
|
35525
35638
|
};
|
|
35526
|
-
await mkdir3(
|
|
35639
|
+
await mkdir3(path17.dirname(sentinelPath), { recursive: true });
|
|
35527
35640
|
await writeFile3(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
35528
35641
|
}
|
|
35529
35642
|
|
|
@@ -35760,15 +35873,15 @@ async function handlePlanCommand(directory, args) {
|
|
|
35760
35873
|
init_manager();
|
|
35761
35874
|
init_manager2();
|
|
35762
35875
|
import * as fs13 from "fs";
|
|
35763
|
-
import * as
|
|
35876
|
+
import * as path23 from "path";
|
|
35764
35877
|
|
|
35765
35878
|
// src/tools/lint.ts
|
|
35766
35879
|
import * as fs9 from "fs";
|
|
35767
|
-
import * as
|
|
35880
|
+
import * as path19 from "path";
|
|
35768
35881
|
|
|
35769
35882
|
// src/build/discovery.ts
|
|
35770
35883
|
import * as fs8 from "fs";
|
|
35771
|
-
import * as
|
|
35884
|
+
import * as path18 from "path";
|
|
35772
35885
|
|
|
35773
35886
|
// src/lang/detector.ts
|
|
35774
35887
|
import { access, readdir as readdir2 } from "fs/promises";
|
|
@@ -36865,11 +36978,11 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36865
36978
|
const regex = simpleGlobToRegex(pattern);
|
|
36866
36979
|
const matches = files.filter((f) => regex.test(f));
|
|
36867
36980
|
if (matches.length > 0) {
|
|
36868
|
-
return
|
|
36981
|
+
return path18.join(dir, matches[0]);
|
|
36869
36982
|
}
|
|
36870
36983
|
} catch {}
|
|
36871
36984
|
} else {
|
|
36872
|
-
const filePath =
|
|
36985
|
+
const filePath = path18.join(workingDir, pattern);
|
|
36873
36986
|
if (fs8.existsSync(filePath)) {
|
|
36874
36987
|
return filePath;
|
|
36875
36988
|
}
|
|
@@ -36878,7 +36991,7 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36878
36991
|
return null;
|
|
36879
36992
|
}
|
|
36880
36993
|
function getRepoDefinedScripts(workingDir, scripts) {
|
|
36881
|
-
const packageJsonPath =
|
|
36994
|
+
const packageJsonPath = path18.join(workingDir, "package.json");
|
|
36882
36995
|
if (!fs8.existsSync(packageJsonPath)) {
|
|
36883
36996
|
return [];
|
|
36884
36997
|
}
|
|
@@ -36919,7 +37032,7 @@ function findAllBuildFiles(workingDir) {
|
|
|
36919
37032
|
const regex = simpleGlobToRegex(pattern);
|
|
36920
37033
|
findFilesRecursive(workingDir, regex, allBuildFiles);
|
|
36921
37034
|
} else {
|
|
36922
|
-
const filePath =
|
|
37035
|
+
const filePath = path18.join(workingDir, pattern);
|
|
36923
37036
|
if (fs8.existsSync(filePath)) {
|
|
36924
37037
|
allBuildFiles.add(filePath);
|
|
36925
37038
|
}
|
|
@@ -36932,7 +37045,7 @@ function findFilesRecursive(dir, regex, results) {
|
|
|
36932
37045
|
try {
|
|
36933
37046
|
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
36934
37047
|
for (const entry of entries) {
|
|
36935
|
-
const fullPath =
|
|
37048
|
+
const fullPath = path18.join(dir, entry.name);
|
|
36936
37049
|
if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
|
|
36937
37050
|
findFilesRecursive(fullPath, regex, results);
|
|
36938
37051
|
} else if (entry.isFile() && regex.test(entry.name)) {
|
|
@@ -36955,7 +37068,7 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
|
|
|
36955
37068
|
let foundCommand = false;
|
|
36956
37069
|
for (const cmd of sortedCommands) {
|
|
36957
37070
|
if (cmd.detectFile) {
|
|
36958
|
-
const detectFilePath =
|
|
37071
|
+
const detectFilePath = path18.join(workingDir, cmd.detectFile);
|
|
36959
37072
|
if (!fs8.existsSync(detectFilePath)) {
|
|
36960
37073
|
continue;
|
|
36961
37074
|
}
|
|
@@ -37103,9 +37216,9 @@ function validateArgs(args) {
|
|
|
37103
37216
|
}
|
|
37104
37217
|
function getLinterCommand(linter, mode, projectDir) {
|
|
37105
37218
|
const isWindows = process.platform === "win32";
|
|
37106
|
-
const binDir =
|
|
37107
|
-
const biomeBin = isWindows ?
|
|
37108
|
-
const eslintBin = isWindows ?
|
|
37219
|
+
const binDir = path19.join(projectDir, "node_modules", ".bin");
|
|
37220
|
+
const biomeBin = isWindows ? path19.join(binDir, "biome.EXE") : path19.join(binDir, "biome");
|
|
37221
|
+
const eslintBin = isWindows ? path19.join(binDir, "eslint.cmd") : path19.join(binDir, "eslint");
|
|
37109
37222
|
switch (linter) {
|
|
37110
37223
|
case "biome":
|
|
37111
37224
|
if (mode === "fix") {
|
|
@@ -37121,7 +37234,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
37121
37234
|
}
|
|
37122
37235
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
37123
37236
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
37124
|
-
const gradlew = fs9.existsSync(
|
|
37237
|
+
const gradlew = fs9.existsSync(path19.join(cwd, gradlewName)) ? path19.join(cwd, gradlewName) : null;
|
|
37125
37238
|
switch (linter) {
|
|
37126
37239
|
case "ruff":
|
|
37127
37240
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -37155,10 +37268,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
37155
37268
|
}
|
|
37156
37269
|
}
|
|
37157
37270
|
function detectRuff(cwd) {
|
|
37158
|
-
if (fs9.existsSync(
|
|
37271
|
+
if (fs9.existsSync(path19.join(cwd, "ruff.toml")))
|
|
37159
37272
|
return isCommandAvailable("ruff");
|
|
37160
37273
|
try {
|
|
37161
|
-
const pyproject =
|
|
37274
|
+
const pyproject = path19.join(cwd, "pyproject.toml");
|
|
37162
37275
|
if (fs9.existsSync(pyproject)) {
|
|
37163
37276
|
const content = fs9.readFileSync(pyproject, "utf-8");
|
|
37164
37277
|
if (content.includes("[tool.ruff]"))
|
|
@@ -37168,19 +37281,19 @@ function detectRuff(cwd) {
|
|
|
37168
37281
|
return false;
|
|
37169
37282
|
}
|
|
37170
37283
|
function detectClippy(cwd) {
|
|
37171
|
-
return fs9.existsSync(
|
|
37284
|
+
return fs9.existsSync(path19.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
37172
37285
|
}
|
|
37173
37286
|
function detectGolangciLint(cwd) {
|
|
37174
|
-
return fs9.existsSync(
|
|
37287
|
+
return fs9.existsSync(path19.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
37175
37288
|
}
|
|
37176
37289
|
function detectCheckstyle(cwd) {
|
|
37177
|
-
const hasMaven = fs9.existsSync(
|
|
37178
|
-
const hasGradle = fs9.existsSync(
|
|
37179
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs9.existsSync(
|
|
37290
|
+
const hasMaven = fs9.existsSync(path19.join(cwd, "pom.xml"));
|
|
37291
|
+
const hasGradle = fs9.existsSync(path19.join(cwd, "build.gradle")) || fs9.existsSync(path19.join(cwd, "build.gradle.kts"));
|
|
37292
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs9.existsSync(path19.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
37180
37293
|
return (hasMaven || hasGradle) && hasBinary;
|
|
37181
37294
|
}
|
|
37182
37295
|
function detectKtlint(cwd) {
|
|
37183
|
-
const hasKotlin = fs9.existsSync(
|
|
37296
|
+
const hasKotlin = fs9.existsSync(path19.join(cwd, "build.gradle.kts")) || fs9.existsSync(path19.join(cwd, "build.gradle")) || (() => {
|
|
37184
37297
|
try {
|
|
37185
37298
|
return fs9.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
37186
37299
|
} catch {
|
|
@@ -37199,11 +37312,11 @@ function detectDotnetFormat(cwd) {
|
|
|
37199
37312
|
}
|
|
37200
37313
|
}
|
|
37201
37314
|
function detectCppcheck(cwd) {
|
|
37202
|
-
if (fs9.existsSync(
|
|
37315
|
+
if (fs9.existsSync(path19.join(cwd, "CMakeLists.txt"))) {
|
|
37203
37316
|
return isCommandAvailable("cppcheck");
|
|
37204
37317
|
}
|
|
37205
37318
|
try {
|
|
37206
|
-
const dirsToCheck = [cwd,
|
|
37319
|
+
const dirsToCheck = [cwd, path19.join(cwd, "src")];
|
|
37207
37320
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
37208
37321
|
try {
|
|
37209
37322
|
return fs9.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
@@ -37217,13 +37330,13 @@ function detectCppcheck(cwd) {
|
|
|
37217
37330
|
}
|
|
37218
37331
|
}
|
|
37219
37332
|
function detectSwiftlint(cwd) {
|
|
37220
|
-
return fs9.existsSync(
|
|
37333
|
+
return fs9.existsSync(path19.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
37221
37334
|
}
|
|
37222
37335
|
function detectDartAnalyze(cwd) {
|
|
37223
|
-
return fs9.existsSync(
|
|
37336
|
+
return fs9.existsSync(path19.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
37224
37337
|
}
|
|
37225
37338
|
function detectRubocop(cwd) {
|
|
37226
|
-
return (fs9.existsSync(
|
|
37339
|
+
return (fs9.existsSync(path19.join(cwd, "Gemfile")) || fs9.existsSync(path19.join(cwd, "gems.rb")) || fs9.existsSync(path19.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
|
|
37227
37340
|
}
|
|
37228
37341
|
function detectAdditionalLinter(cwd) {
|
|
37229
37342
|
if (detectRuff(cwd))
|
|
@@ -37251,10 +37364,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
37251
37364
|
function findBinInAncestors(startDir, binName) {
|
|
37252
37365
|
let dir = startDir;
|
|
37253
37366
|
while (true) {
|
|
37254
|
-
const candidate =
|
|
37367
|
+
const candidate = path19.join(dir, "node_modules", ".bin", binName);
|
|
37255
37368
|
if (fs9.existsSync(candidate))
|
|
37256
37369
|
return candidate;
|
|
37257
|
-
const parent =
|
|
37370
|
+
const parent = path19.dirname(dir);
|
|
37258
37371
|
if (parent === dir)
|
|
37259
37372
|
break;
|
|
37260
37373
|
dir = parent;
|
|
@@ -37263,10 +37376,10 @@ function findBinInAncestors(startDir, binName) {
|
|
|
37263
37376
|
}
|
|
37264
37377
|
function findBinInEnvPath(binName) {
|
|
37265
37378
|
const searchPath = process.env.PATH ?? "";
|
|
37266
|
-
for (const dir of searchPath.split(
|
|
37379
|
+
for (const dir of searchPath.split(path19.delimiter)) {
|
|
37267
37380
|
if (!dir)
|
|
37268
37381
|
continue;
|
|
37269
|
-
const candidate =
|
|
37382
|
+
const candidate = path19.join(dir, binName);
|
|
37270
37383
|
if (fs9.existsSync(candidate))
|
|
37271
37384
|
return candidate;
|
|
37272
37385
|
}
|
|
@@ -37279,13 +37392,13 @@ async function detectAvailableLinter(directory) {
|
|
|
37279
37392
|
return null;
|
|
37280
37393
|
const projectDir = directory;
|
|
37281
37394
|
const isWindows = process.platform === "win32";
|
|
37282
|
-
const biomeBin = isWindows ?
|
|
37283
|
-
const eslintBin = isWindows ?
|
|
37395
|
+
const biomeBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "biome.EXE") : path19.join(projectDir, "node_modules", ".bin", "biome");
|
|
37396
|
+
const eslintBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path19.join(projectDir, "node_modules", ".bin", "eslint");
|
|
37284
37397
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
37285
37398
|
if (localResult)
|
|
37286
37399
|
return localResult;
|
|
37287
|
-
const biomeAncestor = findBinInAncestors(
|
|
37288
|
-
const eslintAncestor = findBinInAncestors(
|
|
37400
|
+
const biomeAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
37401
|
+
const eslintAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
37289
37402
|
if (biomeAncestor || eslintAncestor) {
|
|
37290
37403
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
37291
37404
|
}
|
|
@@ -37493,7 +37606,7 @@ For Rust: rustup component add clippy`
|
|
|
37493
37606
|
|
|
37494
37607
|
// src/tools/secretscan.ts
|
|
37495
37608
|
import * as fs10 from "fs";
|
|
37496
|
-
import * as
|
|
37609
|
+
import * as path20 from "path";
|
|
37497
37610
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
37498
37611
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
37499
37612
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -37720,7 +37833,7 @@ function isGlobOrPathPattern(pattern) {
|
|
|
37720
37833
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
37721
37834
|
}
|
|
37722
37835
|
function loadSecretScanIgnore(scanDir) {
|
|
37723
|
-
const ignorePath =
|
|
37836
|
+
const ignorePath = path20.join(scanDir, ".secretscanignore");
|
|
37724
37837
|
try {
|
|
37725
37838
|
if (!fs10.existsSync(ignorePath))
|
|
37726
37839
|
return [];
|
|
@@ -37743,7 +37856,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
37743
37856
|
if (exactNames.has(entry))
|
|
37744
37857
|
return true;
|
|
37745
37858
|
for (const pattern of globPatterns) {
|
|
37746
|
-
if (
|
|
37859
|
+
if (path20.matchesGlob(relPath, pattern))
|
|
37747
37860
|
return true;
|
|
37748
37861
|
}
|
|
37749
37862
|
return false;
|
|
@@ -37764,7 +37877,7 @@ function validateDirectoryInput(dir) {
|
|
|
37764
37877
|
return null;
|
|
37765
37878
|
}
|
|
37766
37879
|
function isBinaryFile(filePath, buffer) {
|
|
37767
|
-
const ext =
|
|
37880
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
37768
37881
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37769
37882
|
return true;
|
|
37770
37883
|
}
|
|
@@ -37901,9 +38014,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
37901
38014
|
return false;
|
|
37902
38015
|
}
|
|
37903
38016
|
function isPathWithinScope(realPath, scanDir) {
|
|
37904
|
-
const resolvedScanDir =
|
|
37905
|
-
const resolvedRealPath =
|
|
37906
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
38017
|
+
const resolvedScanDir = path20.resolve(scanDir);
|
|
38018
|
+
const resolvedRealPath = path20.resolve(realPath);
|
|
38019
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path20.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
37907
38020
|
}
|
|
37908
38021
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
37909
38022
|
skippedDirs: 0,
|
|
@@ -37929,8 +38042,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37929
38042
|
return a.localeCompare(b);
|
|
37930
38043
|
});
|
|
37931
38044
|
for (const entry of entries) {
|
|
37932
|
-
const fullPath =
|
|
37933
|
-
const relPath =
|
|
38045
|
+
const fullPath = path20.join(dir, entry);
|
|
38046
|
+
const relPath = path20.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
37934
38047
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
37935
38048
|
stats.skippedDirs++;
|
|
37936
38049
|
continue;
|
|
@@ -37965,7 +38078,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37965
38078
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
37966
38079
|
files.push(...subFiles);
|
|
37967
38080
|
} else if (lstat.isFile()) {
|
|
37968
|
-
const ext =
|
|
38081
|
+
const ext = path20.extname(fullPath).toLowerCase();
|
|
37969
38082
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37970
38083
|
files.push(fullPath);
|
|
37971
38084
|
} else {
|
|
@@ -38031,7 +38144,7 @@ var secretscan = createSwarmTool({
|
|
|
38031
38144
|
}
|
|
38032
38145
|
}
|
|
38033
38146
|
try {
|
|
38034
|
-
const _scanDirRaw =
|
|
38147
|
+
const _scanDirRaw = path20.resolve(directory);
|
|
38035
38148
|
const scanDir = (() => {
|
|
38036
38149
|
try {
|
|
38037
38150
|
return fs10.realpathSync(_scanDirRaw);
|
|
@@ -38189,11 +38302,11 @@ async function runSecretscan(directory) {
|
|
|
38189
38302
|
|
|
38190
38303
|
// src/tools/test-runner.ts
|
|
38191
38304
|
import * as fs12 from "fs";
|
|
38192
|
-
import * as
|
|
38305
|
+
import * as path22 from "path";
|
|
38193
38306
|
|
|
38194
38307
|
// src/tools/resolve-working-directory.ts
|
|
38195
38308
|
import * as fs11 from "fs";
|
|
38196
|
-
import * as
|
|
38309
|
+
import * as path21 from "path";
|
|
38197
38310
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
38198
38311
|
if (workingDirectory == null || workingDirectory === "") {
|
|
38199
38312
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -38213,15 +38326,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
38213
38326
|
};
|
|
38214
38327
|
}
|
|
38215
38328
|
}
|
|
38216
|
-
const normalizedDir =
|
|
38217
|
-
const pathParts = normalizedDir.split(
|
|
38329
|
+
const normalizedDir = path21.normalize(workingDirectory);
|
|
38330
|
+
const pathParts = normalizedDir.split(path21.sep);
|
|
38218
38331
|
if (pathParts.includes("..")) {
|
|
38219
38332
|
return {
|
|
38220
38333
|
success: false,
|
|
38221
38334
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
38222
38335
|
};
|
|
38223
38336
|
}
|
|
38224
|
-
const resolvedDir =
|
|
38337
|
+
const resolvedDir = path21.resolve(normalizedDir);
|
|
38225
38338
|
try {
|
|
38226
38339
|
const realPath = fs11.realpathSync(resolvedDir);
|
|
38227
38340
|
return { success: true, directory: realPath };
|
|
@@ -38304,14 +38417,14 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
38304
38417
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
38305
38418
|
}
|
|
38306
38419
|
function detectGoTest(cwd) {
|
|
38307
|
-
return fs12.existsSync(
|
|
38420
|
+
return fs12.existsSync(path22.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
38308
38421
|
}
|
|
38309
38422
|
function detectJavaMaven(cwd) {
|
|
38310
|
-
return fs12.existsSync(
|
|
38423
|
+
return fs12.existsSync(path22.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
38311
38424
|
}
|
|
38312
38425
|
function detectGradle(cwd) {
|
|
38313
|
-
const hasBuildFile = fs12.existsSync(
|
|
38314
|
-
const hasGradlew = fs12.existsSync(
|
|
38426
|
+
const hasBuildFile = fs12.existsSync(path22.join(cwd, "build.gradle")) || fs12.existsSync(path22.join(cwd, "build.gradle.kts"));
|
|
38427
|
+
const hasGradlew = fs12.existsSync(path22.join(cwd, "gradlew")) || fs12.existsSync(path22.join(cwd, "gradlew.bat"));
|
|
38315
38428
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
38316
38429
|
}
|
|
38317
38430
|
function detectDotnetTest(cwd) {
|
|
@@ -38324,30 +38437,30 @@ function detectDotnetTest(cwd) {
|
|
|
38324
38437
|
}
|
|
38325
38438
|
}
|
|
38326
38439
|
function detectCTest(cwd) {
|
|
38327
|
-
const hasSource = fs12.existsSync(
|
|
38328
|
-
const hasBuildCache = fs12.existsSync(
|
|
38440
|
+
const hasSource = fs12.existsSync(path22.join(cwd, "CMakeLists.txt"));
|
|
38441
|
+
const hasBuildCache = fs12.existsSync(path22.join(cwd, "CMakeCache.txt")) || fs12.existsSync(path22.join(cwd, "build", "CMakeCache.txt"));
|
|
38329
38442
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
38330
38443
|
}
|
|
38331
38444
|
function detectSwiftTest(cwd) {
|
|
38332
|
-
return fs12.existsSync(
|
|
38445
|
+
return fs12.existsSync(path22.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
38333
38446
|
}
|
|
38334
38447
|
function detectDartTest(cwd) {
|
|
38335
|
-
return fs12.existsSync(
|
|
38448
|
+
return fs12.existsSync(path22.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
38336
38449
|
}
|
|
38337
38450
|
function detectRSpec(cwd) {
|
|
38338
|
-
const hasRSpecFile = fs12.existsSync(
|
|
38339
|
-
const hasGemfile = fs12.existsSync(
|
|
38340
|
-
const hasSpecDir = fs12.existsSync(
|
|
38451
|
+
const hasRSpecFile = fs12.existsSync(path22.join(cwd, ".rspec"));
|
|
38452
|
+
const hasGemfile = fs12.existsSync(path22.join(cwd, "Gemfile"));
|
|
38453
|
+
const hasSpecDir = fs12.existsSync(path22.join(cwd, "spec"));
|
|
38341
38454
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
38342
38455
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
38343
38456
|
}
|
|
38344
38457
|
function detectMinitest(cwd) {
|
|
38345
|
-
return fs12.existsSync(
|
|
38458
|
+
return fs12.existsSync(path22.join(cwd, "test")) && (fs12.existsSync(path22.join(cwd, "Gemfile")) || fs12.existsSync(path22.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
38346
38459
|
}
|
|
38347
38460
|
async function detectTestFramework(cwd) {
|
|
38348
38461
|
const baseDir = cwd;
|
|
38349
38462
|
try {
|
|
38350
|
-
const packageJsonPath =
|
|
38463
|
+
const packageJsonPath = path22.join(baseDir, "package.json");
|
|
38351
38464
|
if (fs12.existsSync(packageJsonPath)) {
|
|
38352
38465
|
const content = fs12.readFileSync(packageJsonPath, "utf-8");
|
|
38353
38466
|
const pkg = JSON.parse(content);
|
|
@@ -38368,16 +38481,16 @@ async function detectTestFramework(cwd) {
|
|
|
38368
38481
|
return "jest";
|
|
38369
38482
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
38370
38483
|
return "mocha";
|
|
38371
|
-
if (fs12.existsSync(
|
|
38484
|
+
if (fs12.existsSync(path22.join(baseDir, "bun.lockb")) || fs12.existsSync(path22.join(baseDir, "bun.lock"))) {
|
|
38372
38485
|
if (scripts.test?.includes("bun"))
|
|
38373
38486
|
return "bun";
|
|
38374
38487
|
}
|
|
38375
38488
|
}
|
|
38376
38489
|
} catch {}
|
|
38377
38490
|
try {
|
|
38378
|
-
const pyprojectTomlPath =
|
|
38379
|
-
const setupCfgPath =
|
|
38380
|
-
const requirementsTxtPath =
|
|
38491
|
+
const pyprojectTomlPath = path22.join(baseDir, "pyproject.toml");
|
|
38492
|
+
const setupCfgPath = path22.join(baseDir, "setup.cfg");
|
|
38493
|
+
const requirementsTxtPath = path22.join(baseDir, "requirements.txt");
|
|
38381
38494
|
if (fs12.existsSync(pyprojectTomlPath)) {
|
|
38382
38495
|
const content = fs12.readFileSync(pyprojectTomlPath, "utf-8");
|
|
38383
38496
|
if (content.includes("[tool.pytest"))
|
|
@@ -38397,7 +38510,7 @@ async function detectTestFramework(cwd) {
|
|
|
38397
38510
|
}
|
|
38398
38511
|
} catch {}
|
|
38399
38512
|
try {
|
|
38400
|
-
const cargoTomlPath =
|
|
38513
|
+
const cargoTomlPath = path22.join(baseDir, "Cargo.toml");
|
|
38401
38514
|
if (fs12.existsSync(cargoTomlPath)) {
|
|
38402
38515
|
const content = fs12.readFileSync(cargoTomlPath, "utf-8");
|
|
38403
38516
|
if (content.includes("[dev-dependencies]")) {
|
|
@@ -38408,9 +38521,9 @@ async function detectTestFramework(cwd) {
|
|
|
38408
38521
|
}
|
|
38409
38522
|
} catch {}
|
|
38410
38523
|
try {
|
|
38411
|
-
const pesterConfigPath =
|
|
38412
|
-
const pesterConfigJsonPath =
|
|
38413
|
-
const pesterPs1Path =
|
|
38524
|
+
const pesterConfigPath = path22.join(baseDir, "pester.config.ps1");
|
|
38525
|
+
const pesterConfigJsonPath = path22.join(baseDir, "pester.config.ps1.json");
|
|
38526
|
+
const pesterPs1Path = path22.join(baseDir, "tests.ps1");
|
|
38414
38527
|
if (fs12.existsSync(pesterConfigPath) || fs12.existsSync(pesterConfigJsonPath) || fs12.existsSync(pesterPs1Path)) {
|
|
38415
38528
|
return "pester";
|
|
38416
38529
|
}
|
|
@@ -38462,8 +38575,8 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38462
38575
|
const testFiles = [];
|
|
38463
38576
|
for (const file3 of sourceFiles) {
|
|
38464
38577
|
const normalizedPath = file3.replace(/\\/g, "/");
|
|
38465
|
-
const basename4 =
|
|
38466
|
-
const dirname10 =
|
|
38578
|
+
const basename4 = path22.basename(file3);
|
|
38579
|
+
const dirname10 = path22.dirname(file3);
|
|
38467
38580
|
if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
|
|
38468
38581
|
if (!testFiles.includes(file3)) {
|
|
38469
38582
|
testFiles.push(file3);
|
|
@@ -38472,13 +38585,13 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38472
38585
|
}
|
|
38473
38586
|
for (const _pattern of TEST_PATTERNS) {
|
|
38474
38587
|
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
38475
|
-
const ext =
|
|
38588
|
+
const ext = path22.extname(basename4);
|
|
38476
38589
|
const possibleTestFiles = [
|
|
38477
|
-
|
|
38478
|
-
|
|
38479
|
-
|
|
38480
|
-
|
|
38481
|
-
|
|
38590
|
+
path22.join(dirname10, `${nameWithoutExt}.spec${ext}`),
|
|
38591
|
+
path22.join(dirname10, `${nameWithoutExt}.test${ext}`),
|
|
38592
|
+
path22.join(dirname10, "__tests__", `${nameWithoutExt}${ext}`),
|
|
38593
|
+
path22.join(dirname10, "tests", `${nameWithoutExt}${ext}`),
|
|
38594
|
+
path22.join(dirname10, "test", `${nameWithoutExt}${ext}`)
|
|
38482
38595
|
];
|
|
38483
38596
|
for (const testFile of possibleTestFiles) {
|
|
38484
38597
|
if (fs12.existsSync(testFile) && !testFiles.includes(testFile)) {
|
|
@@ -38498,7 +38611,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38498
38611
|
for (const testFile of candidateTestFiles) {
|
|
38499
38612
|
try {
|
|
38500
38613
|
const content = fs12.readFileSync(testFile, "utf-8");
|
|
38501
|
-
const testDir =
|
|
38614
|
+
const testDir = path22.dirname(testFile);
|
|
38502
38615
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
38503
38616
|
let match;
|
|
38504
38617
|
match = importRegex.exec(content);
|
|
@@ -38506,8 +38619,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38506
38619
|
const importPath = match[1];
|
|
38507
38620
|
let resolvedImport;
|
|
38508
38621
|
if (importPath.startsWith(".")) {
|
|
38509
|
-
resolvedImport =
|
|
38510
|
-
const existingExt =
|
|
38622
|
+
resolvedImport = path22.resolve(testDir, importPath);
|
|
38623
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38511
38624
|
if (!existingExt) {
|
|
38512
38625
|
for (const extToTry of [
|
|
38513
38626
|
".ts",
|
|
@@ -38527,12 +38640,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38527
38640
|
} else {
|
|
38528
38641
|
continue;
|
|
38529
38642
|
}
|
|
38530
|
-
const importBasename =
|
|
38531
|
-
const importDir =
|
|
38643
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38644
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38532
38645
|
for (const sourceFile of sourceFiles) {
|
|
38533
|
-
const sourceDir =
|
|
38534
|
-
const sourceBasename =
|
|
38535
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38646
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38647
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38648
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38536
38649
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38537
38650
|
if (!testFiles.includes(testFile)) {
|
|
38538
38651
|
testFiles.push(testFile);
|
|
@@ -38547,8 +38660,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38547
38660
|
while (match !== null) {
|
|
38548
38661
|
const importPath = match[1];
|
|
38549
38662
|
if (importPath.startsWith(".")) {
|
|
38550
|
-
let resolvedImport =
|
|
38551
|
-
const existingExt =
|
|
38663
|
+
let resolvedImport = path22.resolve(testDir, importPath);
|
|
38664
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38552
38665
|
if (!existingExt) {
|
|
38553
38666
|
for (const extToTry of [
|
|
38554
38667
|
".ts",
|
|
@@ -38565,12 +38678,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38565
38678
|
}
|
|
38566
38679
|
}
|
|
38567
38680
|
}
|
|
38568
|
-
const importDir =
|
|
38569
|
-
const importBasename =
|
|
38681
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38682
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38570
38683
|
for (const sourceFile of sourceFiles) {
|
|
38571
|
-
const sourceDir =
|
|
38572
|
-
const sourceBasename =
|
|
38573
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38684
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38685
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38686
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38574
38687
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38575
38688
|
if (!testFiles.includes(testFile)) {
|
|
38576
38689
|
testFiles.push(testFile);
|
|
@@ -38655,8 +38768,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38655
38768
|
return ["mvn", "test"];
|
|
38656
38769
|
case "gradle": {
|
|
38657
38770
|
const isWindows = process.platform === "win32";
|
|
38658
|
-
const hasGradlewBat = fs12.existsSync(
|
|
38659
|
-
const hasGradlew = fs12.existsSync(
|
|
38771
|
+
const hasGradlewBat = fs12.existsSync(path22.join(baseDir, "gradlew.bat"));
|
|
38772
|
+
const hasGradlew = fs12.existsSync(path22.join(baseDir, "gradlew"));
|
|
38660
38773
|
if (hasGradlewBat && isWindows)
|
|
38661
38774
|
return ["gradlew.bat", "test"];
|
|
38662
38775
|
if (hasGradlew)
|
|
@@ -38673,7 +38786,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38673
38786
|
"cmake-build-release",
|
|
38674
38787
|
"out"
|
|
38675
38788
|
];
|
|
38676
|
-
const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(
|
|
38789
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(path22.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
38677
38790
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
38678
38791
|
}
|
|
38679
38792
|
case "swift-test":
|
|
@@ -39215,7 +39328,7 @@ var test_runner = createSwarmTool({
|
|
|
39215
39328
|
let effectiveScope = scope;
|
|
39216
39329
|
if (scope === "all") {} else if (scope === "convention") {
|
|
39217
39330
|
const sourceFiles = args.files.filter((f) => {
|
|
39218
|
-
const ext =
|
|
39331
|
+
const ext = path22.extname(f).toLowerCase();
|
|
39219
39332
|
return SOURCE_EXTENSIONS.has(ext);
|
|
39220
39333
|
});
|
|
39221
39334
|
if (sourceFiles.length === 0) {
|
|
@@ -39231,7 +39344,7 @@ var test_runner = createSwarmTool({
|
|
|
39231
39344
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
39232
39345
|
} else if (scope === "graph") {
|
|
39233
39346
|
const sourceFiles = args.files.filter((f) => {
|
|
39234
|
-
const ext =
|
|
39347
|
+
const ext = path22.extname(f).toLowerCase();
|
|
39235
39348
|
return SOURCE_EXTENSIONS.has(ext);
|
|
39236
39349
|
});
|
|
39237
39350
|
if (sourceFiles.length === 0) {
|
|
@@ -39302,8 +39415,8 @@ function validateDirectoryPath(dir) {
|
|
|
39302
39415
|
if (dir.includes("..")) {
|
|
39303
39416
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
39304
39417
|
}
|
|
39305
|
-
const normalized =
|
|
39306
|
-
const absolutePath =
|
|
39418
|
+
const normalized = path23.normalize(dir);
|
|
39419
|
+
const absolutePath = path23.isAbsolute(normalized) ? normalized : path23.resolve(normalized);
|
|
39307
39420
|
return absolutePath;
|
|
39308
39421
|
}
|
|
39309
39422
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -39326,7 +39439,7 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
39326
39439
|
}
|
|
39327
39440
|
function getPackageVersion(dir) {
|
|
39328
39441
|
try {
|
|
39329
|
-
const packagePath =
|
|
39442
|
+
const packagePath = path23.join(dir, "package.json");
|
|
39330
39443
|
if (fs13.existsSync(packagePath)) {
|
|
39331
39444
|
const content = fs13.readFileSync(packagePath, "utf-8");
|
|
39332
39445
|
const pkg = JSON.parse(content);
|
|
@@ -39337,7 +39450,7 @@ function getPackageVersion(dir) {
|
|
|
39337
39450
|
}
|
|
39338
39451
|
function getChangelogVersion(dir) {
|
|
39339
39452
|
try {
|
|
39340
|
-
const changelogPath =
|
|
39453
|
+
const changelogPath = path23.join(dir, "CHANGELOG.md");
|
|
39341
39454
|
if (fs13.existsSync(changelogPath)) {
|
|
39342
39455
|
const content = fs13.readFileSync(changelogPath, "utf-8");
|
|
39343
39456
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
@@ -39351,7 +39464,7 @@ function getChangelogVersion(dir) {
|
|
|
39351
39464
|
function getVersionFileVersion(dir) {
|
|
39352
39465
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
39353
39466
|
for (const file3 of possibleFiles) {
|
|
39354
|
-
const filePath =
|
|
39467
|
+
const filePath = path23.join(dir, file3);
|
|
39355
39468
|
if (fs13.existsSync(filePath)) {
|
|
39356
39469
|
try {
|
|
39357
39470
|
const content = fs13.readFileSync(filePath, "utf-8").trim();
|
|
@@ -39849,7 +39962,7 @@ async function handlePreflightCommand(directory, _args) {
|
|
|
39849
39962
|
// src/knowledge/hive-promoter.ts
|
|
39850
39963
|
import * as fs14 from "fs";
|
|
39851
39964
|
import * as os5 from "os";
|
|
39852
|
-
import * as
|
|
39965
|
+
import * as path24 from "path";
|
|
39853
39966
|
var DANGEROUS_PATTERNS = [
|
|
39854
39967
|
[/rm\s+-rf/, "rm\\s+-rf"],
|
|
39855
39968
|
[/:\s*!\s*\|/, ":\\s*!\\s*\\|"],
|
|
@@ -39895,13 +40008,13 @@ function getHiveFilePath() {
|
|
|
39895
40008
|
const home = os5.homedir();
|
|
39896
40009
|
let dataDir;
|
|
39897
40010
|
if (platform === "win32") {
|
|
39898
|
-
dataDir =
|
|
40011
|
+
dataDir = path24.join(process.env.LOCALAPPDATA || path24.join(home, "AppData", "Local"), "opencode-swarm", "Data");
|
|
39899
40012
|
} else if (platform === "darwin") {
|
|
39900
|
-
dataDir =
|
|
40013
|
+
dataDir = path24.join(home, "Library", "Application Support", "opencode-swarm");
|
|
39901
40014
|
} else {
|
|
39902
|
-
dataDir =
|
|
40015
|
+
dataDir = path24.join(process.env.XDG_DATA_HOME || path24.join(home, ".local", "share"), "opencode-swarm");
|
|
39903
40016
|
}
|
|
39904
|
-
return
|
|
40017
|
+
return path24.join(dataDir, "hive-knowledge.jsonl");
|
|
39905
40018
|
}
|
|
39906
40019
|
async function promoteToHive(_directory, lesson, category) {
|
|
39907
40020
|
const trimmed = (lesson ?? "").trim();
|
|
@@ -39913,7 +40026,7 @@ async function promoteToHive(_directory, lesson, category) {
|
|
|
39913
40026
|
throw new Error(`Lesson rejected by validator: ${validation.reason}`);
|
|
39914
40027
|
}
|
|
39915
40028
|
const hivePath = getHiveFilePath();
|
|
39916
|
-
const hiveDir =
|
|
40029
|
+
const hiveDir = path24.dirname(hivePath);
|
|
39917
40030
|
if (!fs14.existsSync(hiveDir)) {
|
|
39918
40031
|
fs14.mkdirSync(hiveDir, { recursive: true });
|
|
39919
40032
|
}
|
|
@@ -39935,7 +40048,7 @@ async function promoteToHive(_directory, lesson, category) {
|
|
|
39935
40048
|
return `Promoted to hive: "${preview}" (confidence: 1.0, source: manual)`;
|
|
39936
40049
|
}
|
|
39937
40050
|
async function promoteFromSwarm(directory, lessonId) {
|
|
39938
|
-
const knowledgePath =
|
|
40051
|
+
const knowledgePath = path24.join(directory, ".swarm", "knowledge.jsonl");
|
|
39939
40052
|
const entries = [];
|
|
39940
40053
|
if (fs14.existsSync(knowledgePath)) {
|
|
39941
40054
|
const content = fs14.readFileSync(knowledgePath, "utf-8");
|
|
@@ -39962,7 +40075,7 @@ async function promoteFromSwarm(directory, lessonId) {
|
|
|
39962
40075
|
throw new Error(`Lesson rejected by validator: ${validation.reason}`);
|
|
39963
40076
|
}
|
|
39964
40077
|
const hivePath = getHiveFilePath();
|
|
39965
|
-
const hiveDir =
|
|
40078
|
+
const hiveDir = path24.dirname(hivePath);
|
|
39966
40079
|
if (!fs14.existsSync(hiveDir)) {
|
|
39967
40080
|
fs14.mkdirSync(hiveDir, { recursive: true });
|
|
39968
40081
|
}
|
|
@@ -40773,7 +40886,7 @@ async function handleResetCommand(directory, args) {
|
|
|
40773
40886
|
// src/commands/reset-session.ts
|
|
40774
40887
|
init_utils2();
|
|
40775
40888
|
import * as fs16 from "fs";
|
|
40776
|
-
import * as
|
|
40889
|
+
import * as path25 from "path";
|
|
40777
40890
|
async function handleResetSessionCommand(directory, _args) {
|
|
40778
40891
|
const results = [];
|
|
40779
40892
|
try {
|
|
@@ -40788,13 +40901,13 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40788
40901
|
results.push("\u274C Failed to delete state.json");
|
|
40789
40902
|
}
|
|
40790
40903
|
try {
|
|
40791
|
-
const sessionDir =
|
|
40904
|
+
const sessionDir = path25.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
40792
40905
|
if (fs16.existsSync(sessionDir)) {
|
|
40793
40906
|
const files = fs16.readdirSync(sessionDir);
|
|
40794
40907
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
40795
40908
|
let deletedCount = 0;
|
|
40796
40909
|
for (const file3 of otherFiles) {
|
|
40797
|
-
const filePath =
|
|
40910
|
+
const filePath = path25.join(sessionDir, file3);
|
|
40798
40911
|
if (fs16.lstatSync(filePath).isFile()) {
|
|
40799
40912
|
fs16.unlinkSync(filePath);
|
|
40800
40913
|
deletedCount++;
|
|
@@ -40824,7 +40937,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40824
40937
|
// src/summaries/manager.ts
|
|
40825
40938
|
init_utils2();
|
|
40826
40939
|
init_utils();
|
|
40827
|
-
import * as
|
|
40940
|
+
import * as path26 from "path";
|
|
40828
40941
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
40829
40942
|
function sanitizeSummaryId(id) {
|
|
40830
40943
|
if (!id || id.length === 0) {
|
|
@@ -40848,7 +40961,7 @@ function sanitizeSummaryId(id) {
|
|
|
40848
40961
|
}
|
|
40849
40962
|
async function loadFullOutput(directory, id) {
|
|
40850
40963
|
const sanitizedId = sanitizeSummaryId(id);
|
|
40851
|
-
const relativePath =
|
|
40964
|
+
const relativePath = path26.join("summaries", `${sanitizedId}.json`);
|
|
40852
40965
|
validateSwarmPath(directory, relativePath);
|
|
40853
40966
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
40854
40967
|
if (content === null) {
|
|
@@ -40902,7 +41015,7 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
40902
41015
|
// src/commands/rollback.ts
|
|
40903
41016
|
init_utils2();
|
|
40904
41017
|
import * as fs17 from "fs";
|
|
40905
|
-
import * as
|
|
41018
|
+
import * as path27 from "path";
|
|
40906
41019
|
async function handleRollbackCommand(directory, args) {
|
|
40907
41020
|
const phaseArg = args[0];
|
|
40908
41021
|
if (!phaseArg) {
|
|
@@ -40960,8 +41073,8 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40960
41073
|
const successes = [];
|
|
40961
41074
|
const failures = [];
|
|
40962
41075
|
for (const file3 of checkpointFiles) {
|
|
40963
|
-
const src =
|
|
40964
|
-
const dest =
|
|
41076
|
+
const src = path27.join(checkpointDir, file3);
|
|
41077
|
+
const dest = path27.join(swarmDir, file3);
|
|
40965
41078
|
try {
|
|
40966
41079
|
fs17.cpSync(src, dest, { recursive: true, force: true });
|
|
40967
41080
|
successes.push(file3);
|
|
@@ -41024,9 +41137,9 @@ async function handleSimulateCommand(directory, args) {
|
|
|
41024
41137
|
const report = reportLines.filter(Boolean).join(`
|
|
41025
41138
|
`);
|
|
41026
41139
|
const fs18 = await import("fs/promises");
|
|
41027
|
-
const
|
|
41028
|
-
const reportPath =
|
|
41029
|
-
await fs18.mkdir(
|
|
41140
|
+
const path28 = await import("path");
|
|
41141
|
+
const reportPath = path28.join(directory, ".swarm", "simulate-report.md");
|
|
41142
|
+
await fs18.mkdir(path28.dirname(reportPath), { recursive: true });
|
|
41030
41143
|
await fs18.writeFile(reportPath, report, "utf-8");
|
|
41031
41144
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
41032
41145
|
}
|
|
@@ -41509,10 +41622,10 @@ function resolveCommand(tokens) {
|
|
|
41509
41622
|
}
|
|
41510
41623
|
|
|
41511
41624
|
// src/cli/index.ts
|
|
41512
|
-
var CONFIG_DIR =
|
|
41513
|
-
var OPENCODE_CONFIG_PATH =
|
|
41514
|
-
var PLUGIN_CONFIG_PATH =
|
|
41515
|
-
var PROMPTS_DIR =
|
|
41625
|
+
var CONFIG_DIR = path28.join(process.env.XDG_CONFIG_HOME || path28.join(os6.homedir(), ".config"), "opencode");
|
|
41626
|
+
var OPENCODE_CONFIG_PATH = path28.join(CONFIG_DIR, "opencode.json");
|
|
41627
|
+
var PLUGIN_CONFIG_PATH = path28.join(CONFIG_DIR, "opencode-swarm.json");
|
|
41628
|
+
var PROMPTS_DIR = path28.join(CONFIG_DIR, "opencode-swarm");
|
|
41516
41629
|
function ensureDir(dir) {
|
|
41517
41630
|
if (!fs18.existsSync(dir)) {
|
|
41518
41631
|
fs18.mkdirSync(dir, { recursive: true });
|
|
@@ -41536,7 +41649,7 @@ async function install() {
|
|
|
41536
41649
|
`);
|
|
41537
41650
|
ensureDir(CONFIG_DIR);
|
|
41538
41651
|
ensureDir(PROMPTS_DIR);
|
|
41539
|
-
const LEGACY_CONFIG_PATH =
|
|
41652
|
+
const LEGACY_CONFIG_PATH = path28.join(CONFIG_DIR, "config.json");
|
|
41540
41653
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41541
41654
|
if (!opencodeConfig) {
|
|
41542
41655
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|