opencode-swarm 6.46.0 → 6.47.1
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 +450 -285
- package/dist/commands/close.d.ts +1 -1
- package/dist/commands/sync-plan.d.ts +3 -1
- package/dist/index.js +744 -578
- package/dist/plan/ledger.d.ts +2 -2
- package/dist/plan/manager.d.ts +12 -0
- package/dist/plan/manager.loadplan-validation-guard.test.d.ts +13 -0
- package/dist/plan/migration-revert.regression.test.d.ts +8 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -16208,20 +16208,22 @@ async function readLedgerEvents(directory) {
|
|
|
16208
16208
|
return [];
|
|
16209
16209
|
}
|
|
16210
16210
|
}
|
|
16211
|
-
async function initLedger(directory, planId) {
|
|
16211
|
+
async function initLedger(directory, planId, initialPlanHash) {
|
|
16212
16212
|
const ledgerPath = getLedgerPath(directory);
|
|
16213
16213
|
const planJsonPath = getPlanJsonPath(directory);
|
|
16214
16214
|
if (fs4.existsSync(ledgerPath)) {
|
|
16215
16215
|
throw new Error("Ledger already initialized. Use appendLedgerEvent to add events.");
|
|
16216
16216
|
}
|
|
16217
|
-
let planHashAfter = "";
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16217
|
+
let planHashAfter = initialPlanHash ?? "";
|
|
16218
|
+
if (!initialPlanHash) {
|
|
16219
|
+
try {
|
|
16220
|
+
if (fs4.existsSync(planJsonPath)) {
|
|
16221
|
+
const content = fs4.readFileSync(planJsonPath, "utf8");
|
|
16222
|
+
const plan = JSON.parse(content);
|
|
16223
|
+
planHashAfter = computePlanHash(plan);
|
|
16224
|
+
}
|
|
16225
|
+
} catch {}
|
|
16226
|
+
}
|
|
16225
16227
|
const event = {
|
|
16226
16228
|
seq: 1,
|
|
16227
16229
|
timestamp: new Date().toISOString(),
|
|
@@ -16233,7 +16235,7 @@ async function initLedger(directory, planId) {
|
|
|
16233
16235
|
schema_version: LEDGER_SCHEMA_VERSION
|
|
16234
16236
|
};
|
|
16235
16237
|
fs4.mkdirSync(path7.join(directory, ".swarm"), { recursive: true });
|
|
16236
|
-
const tempPath = `${ledgerPath}.tmp`;
|
|
16238
|
+
const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
|
|
16237
16239
|
const line = `${JSON.stringify(event)}
|
|
16238
16240
|
`;
|
|
16239
16241
|
fs4.writeFileSync(tempPath, line, "utf8");
|
|
@@ -16260,7 +16262,7 @@ async function appendLedgerEvent(directory, eventInput, options) {
|
|
|
16260
16262
|
schema_version: LEDGER_SCHEMA_VERSION
|
|
16261
16263
|
};
|
|
16262
16264
|
fs4.mkdirSync(path7.join(directory, ".swarm"), { recursive: true });
|
|
16263
|
-
const tempPath = `${ledgerPath}.tmp`;
|
|
16265
|
+
const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
|
|
16264
16266
|
const line = `${JSON.stringify(event)}
|
|
16265
16267
|
`;
|
|
16266
16268
|
if (fs4.existsSync(ledgerPath)) {
|
|
@@ -16278,10 +16280,11 @@ async function takeSnapshotEvent(directory, plan, options) {
|
|
|
16278
16280
|
plan,
|
|
16279
16281
|
payload_hash: payloadHash
|
|
16280
16282
|
};
|
|
16283
|
+
const planId = `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
16281
16284
|
return appendLedgerEvent(directory, {
|
|
16282
16285
|
event_type: "snapshot",
|
|
16283
16286
|
source: "takeSnapshotEvent",
|
|
16284
|
-
plan_id:
|
|
16287
|
+
plan_id: planId,
|
|
16285
16288
|
payload: snapshotPayload
|
|
16286
16289
|
}, options);
|
|
16287
16290
|
}
|
|
@@ -16290,13 +16293,15 @@ async function replayFromLedger(directory, options) {
|
|
|
16290
16293
|
if (events.length === 0) {
|
|
16291
16294
|
return null;
|
|
16292
16295
|
}
|
|
16296
|
+
const targetPlanId = events[0].plan_id;
|
|
16297
|
+
const relevantEvents = events.filter((e) => e.plan_id === targetPlanId);
|
|
16293
16298
|
{
|
|
16294
|
-
const snapshotEvents =
|
|
16299
|
+
const snapshotEvents = relevantEvents.filter((e) => e.event_type === "snapshot");
|
|
16295
16300
|
if (snapshotEvents.length > 0) {
|
|
16296
16301
|
const latestSnapshotEvent = snapshotEvents[snapshotEvents.length - 1];
|
|
16297
16302
|
const snapshotPayload = latestSnapshotEvent.payload;
|
|
16298
16303
|
let plan2 = snapshotPayload.plan;
|
|
16299
|
-
const eventsAfterSnapshot =
|
|
16304
|
+
const eventsAfterSnapshot = relevantEvents.filter((e) => e.seq > latestSnapshotEvent.seq);
|
|
16300
16305
|
for (const event of eventsAfterSnapshot) {
|
|
16301
16306
|
plan2 = applyEventToPlan(plan2, event);
|
|
16302
16307
|
if (plan2 === null) {
|
|
@@ -16317,7 +16322,7 @@ async function replayFromLedger(directory, options) {
|
|
|
16317
16322
|
} catch {
|
|
16318
16323
|
return null;
|
|
16319
16324
|
}
|
|
16320
|
-
for (const event of
|
|
16325
|
+
for (const event of relevantEvents) {
|
|
16321
16326
|
if (plan === null) {
|
|
16322
16327
|
return null;
|
|
16323
16328
|
}
|
|
@@ -16331,10 +16336,14 @@ function applyEventToPlan(plan, event) {
|
|
|
16331
16336
|
return plan;
|
|
16332
16337
|
case "task_status_changed":
|
|
16333
16338
|
if (event.task_id && event.to_status) {
|
|
16339
|
+
const parseResult = TaskStatusSchema.safeParse(event.to_status);
|
|
16340
|
+
if (!parseResult.success) {
|
|
16341
|
+
return plan;
|
|
16342
|
+
}
|
|
16334
16343
|
for (const phase of plan.phases) {
|
|
16335
16344
|
const task = phase.tasks.find((t) => t.id === event.task_id);
|
|
16336
16345
|
if (task) {
|
|
16337
|
-
task.status =
|
|
16346
|
+
task.status = parseResult.data;
|
|
16338
16347
|
break;
|
|
16339
16348
|
}
|
|
16340
16349
|
}
|
|
@@ -16368,6 +16377,7 @@ function applyEventToPlan(plan, event) {
|
|
|
16368
16377
|
}
|
|
16369
16378
|
var LEDGER_SCHEMA_VERSION = "1.0.0", LEDGER_FILENAME = "plan-ledger.jsonl", PLAN_JSON_FILENAME = "plan.json", LedgerStaleWriterError;
|
|
16370
16379
|
var init_ledger = __esm(() => {
|
|
16380
|
+
init_plan_schema();
|
|
16371
16381
|
LedgerStaleWriterError = class LedgerStaleWriterError extends Error {
|
|
16372
16382
|
constructor(message) {
|
|
16373
16383
|
super(message);
|
|
@@ -16377,7 +16387,7 @@ var init_ledger = __esm(() => {
|
|
|
16377
16387
|
});
|
|
16378
16388
|
|
|
16379
16389
|
// src/plan/manager.ts
|
|
16380
|
-
import { renameSync as renameSync3, unlinkSync } from "fs";
|
|
16390
|
+
import { existsSync as existsSync5, renameSync as renameSync3, unlinkSync } from "fs";
|
|
16381
16391
|
import * as path8 from "path";
|
|
16382
16392
|
async function loadPlanJsonOnly(directory) {
|
|
16383
16393
|
const planJsonContent = await readSwarmFileAsync(directory, "plan.json");
|
|
@@ -16508,28 +16518,49 @@ async function loadPlan(directory) {
|
|
|
16508
16518
|
const planHash = computePlanHash(validated);
|
|
16509
16519
|
const ledgerHash = await getLatestLedgerHash(directory);
|
|
16510
16520
|
if (ledgerHash !== "" && planHash !== ledgerHash) {
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
|
|
16521
|
+
const currentPlanId = `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
16522
|
+
const ledgerEvents = await readLedgerEvents(directory);
|
|
16523
|
+
const firstEvent = ledgerEvents.length > 0 ? ledgerEvents[0] : null;
|
|
16524
|
+
if (firstEvent && firstEvent.plan_id !== currentPlanId) {
|
|
16525
|
+
warn(`[loadPlan] Ledger identity mismatch (ledger: ${firstEvent.plan_id}, plan: ${currentPlanId}) \u2014 skipping ledger rebuild (migration detected). Use /swarm reset-session to reinitialize the ledger.`);
|
|
16526
|
+
} else {
|
|
16527
|
+
warn("[loadPlan] plan.json is stale (hash mismatch with ledger) \u2014 rebuilding from ledger. If this recurs, run /swarm reset-session to clear stale session state.");
|
|
16528
|
+
try {
|
|
16529
|
+
const rebuilt = await replayFromLedger(directory);
|
|
16530
|
+
if (rebuilt) {
|
|
16531
|
+
await rebuildPlan(directory, rebuilt);
|
|
16532
|
+
warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at SWARM_PLAN.md if it exists.");
|
|
16533
|
+
return rebuilt;
|
|
16534
|
+
}
|
|
16535
|
+
} catch (replayError) {
|
|
16536
|
+
warn(`[loadPlan] Ledger replay failed during hash-mismatch rebuild: ${replayError instanceof Error ? replayError.message : String(replayError)}. Returning stale plan.json. To recover: check SWARM_PLAN.md for a checkpoint, or run /swarm reset-session.`);
|
|
16518
16537
|
}
|
|
16519
|
-
} catch (replayError) {
|
|
16520
|
-
warn(`[loadPlan] Ledger replay failed during hash-mismatch rebuild: ${replayError instanceof Error ? replayError.message : String(replayError)}. Returning stale plan.json. To recover: check SWARM_PLAN.md for a checkpoint, or run /swarm reset-session.`);
|
|
16521
16538
|
}
|
|
16522
16539
|
}
|
|
16523
16540
|
}
|
|
16524
16541
|
return validated;
|
|
16525
16542
|
} catch (error93) {
|
|
16526
16543
|
warn(`[loadPlan] plan.json validation failed: ${error93 instanceof Error ? error93.message : String(error93)}. Attempting rebuild from ledger. If rebuild fails, check SWARM_PLAN.md for a checkpoint.`);
|
|
16544
|
+
let rawPlanId = null;
|
|
16545
|
+
try {
|
|
16546
|
+
const rawParsed = JSON.parse(planJsonContent);
|
|
16547
|
+
if (typeof rawParsed?.swarm === "string" && typeof rawParsed?.title === "string") {
|
|
16548
|
+
rawPlanId = `${rawParsed.swarm}-${rawParsed.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
16549
|
+
}
|
|
16550
|
+
} catch {}
|
|
16527
16551
|
if (await ledgerExists(directory)) {
|
|
16528
|
-
const
|
|
16529
|
-
|
|
16530
|
-
|
|
16531
|
-
|
|
16532
|
-
|
|
16552
|
+
const ledgerEventsForCatch = await readLedgerEvents(directory);
|
|
16553
|
+
const catchFirstEvent = ledgerEventsForCatch.length > 0 ? ledgerEventsForCatch[0] : null;
|
|
16554
|
+
const identityMatch = rawPlanId === null || catchFirstEvent === null || catchFirstEvent.plan_id === rawPlanId;
|
|
16555
|
+
if (!identityMatch) {
|
|
16556
|
+
warn(`[loadPlan] Ledger identity mismatch in validation-failure path (ledger: ${catchFirstEvent?.plan_id}, plan: ${rawPlanId}) \u2014 skipping ledger rebuild (migration detected).`);
|
|
16557
|
+
} else if (catchFirstEvent !== null && rawPlanId !== null) {
|
|
16558
|
+
const rebuilt = await replayFromLedger(directory);
|
|
16559
|
+
if (rebuilt) {
|
|
16560
|
+
await rebuildPlan(directory, rebuilt);
|
|
16561
|
+
warn("[loadPlan] Rebuilt plan from ledger after validation failure. Projection was stale.");
|
|
16562
|
+
return rebuilt;
|
|
16563
|
+
}
|
|
16533
16564
|
}
|
|
16534
16565
|
}
|
|
16535
16566
|
const planMdContent2 = await readSwarmFileAsync(directory, "plan.md");
|
|
@@ -16597,9 +16628,28 @@ async function savePlan(directory, plan, options) {
|
|
|
16597
16628
|
}
|
|
16598
16629
|
}
|
|
16599
16630
|
const currentPlan = await loadPlanJsonOnly(directory);
|
|
16631
|
+
const planId = `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
16632
|
+
const planHashForInit = computePlanHash(validated);
|
|
16600
16633
|
if (!await ledgerExists(directory)) {
|
|
16601
|
-
|
|
16602
|
-
|
|
16634
|
+
await initLedger(directory, planId, planHashForInit);
|
|
16635
|
+
} else {
|
|
16636
|
+
const existingEvents = await readLedgerEvents(directory);
|
|
16637
|
+
if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
|
|
16638
|
+
const swarmDir2 = path8.resolve(directory, ".swarm");
|
|
16639
|
+
const oldLedgerPath = path8.join(swarmDir2, "plan-ledger.jsonl");
|
|
16640
|
+
const archivePath = path8.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
|
|
16641
|
+
if (existsSync5(oldLedgerPath)) {
|
|
16642
|
+
renameSync3(oldLedgerPath, archivePath);
|
|
16643
|
+
warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
|
|
16644
|
+
}
|
|
16645
|
+
try {
|
|
16646
|
+
await initLedger(directory, planId, planHashForInit);
|
|
16647
|
+
} catch (initErr) {
|
|
16648
|
+
if (!(initErr instanceof Error && initErr.message.includes("already initialized"))) {
|
|
16649
|
+
throw initErr;
|
|
16650
|
+
}
|
|
16651
|
+
}
|
|
16652
|
+
}
|
|
16603
16653
|
}
|
|
16604
16654
|
const currentHash = computeCurrentPlanHash(directory);
|
|
16605
16655
|
const hashAfter = computePlanHash(validated);
|
|
@@ -16692,10 +16742,24 @@ async function rebuildPlan(directory, plan) {
|
|
|
16692
16742
|
const tempPlanPath = path8.join(swarmDir, `plan.json.rebuild.${Date.now()}`);
|
|
16693
16743
|
await Bun.write(tempPlanPath, JSON.stringify(targetPlan, null, 2));
|
|
16694
16744
|
renameSync3(tempPlanPath, planPath);
|
|
16745
|
+
const contentHash = computePlanContentHash(targetPlan);
|
|
16695
16746
|
const markdown = derivePlanMarkdown(targetPlan);
|
|
16747
|
+
const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
|
|
16748
|
+
${markdown}`;
|
|
16696
16749
|
const tempMdPath = path8.join(swarmDir, `plan.md.rebuild.${Date.now()}`);
|
|
16697
|
-
await Bun.write(tempMdPath,
|
|
16750
|
+
await Bun.write(tempMdPath, markdownWithHash);
|
|
16698
16751
|
renameSync3(tempMdPath, mdPath);
|
|
16752
|
+
try {
|
|
16753
|
+
const markerPath = path8.join(swarmDir, ".plan-write-marker");
|
|
16754
|
+
const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
|
|
16755
|
+
const marker = JSON.stringify({
|
|
16756
|
+
source: "plan_manager",
|
|
16757
|
+
timestamp: new Date().toISOString(),
|
|
16758
|
+
phases_count: targetPlan.phases.length,
|
|
16759
|
+
tasks_count: tasksCount
|
|
16760
|
+
});
|
|
16761
|
+
await Bun.write(markerPath, marker);
|
|
16762
|
+
} catch {}
|
|
16699
16763
|
return targetPlan;
|
|
16700
16764
|
}
|
|
16701
16765
|
function derivePlanMarkdown(plan) {
|
|
@@ -16987,13 +17051,13 @@ __export(exports_config_doctor, {
|
|
|
16987
17051
|
import * as crypto3 from "crypto";
|
|
16988
17052
|
import * as fs7 from "fs";
|
|
16989
17053
|
import * as os4 from "os";
|
|
16990
|
-
import * as
|
|
17054
|
+
import * as path16 from "path";
|
|
16991
17055
|
function getUserConfigDir3() {
|
|
16992
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
17056
|
+
return process.env.XDG_CONFIG_HOME || path16.join(os4.homedir(), ".config");
|
|
16993
17057
|
}
|
|
16994
17058
|
function getConfigPaths(directory) {
|
|
16995
|
-
const userConfigPath =
|
|
16996
|
-
const projectConfigPath =
|
|
17059
|
+
const userConfigPath = path16.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
|
|
17060
|
+
const projectConfigPath = path16.join(directory, ".opencode", "opencode-swarm.json");
|
|
16997
17061
|
return { userConfigPath, projectConfigPath };
|
|
16998
17062
|
}
|
|
16999
17063
|
function computeHash(content) {
|
|
@@ -17018,9 +17082,9 @@ function isValidConfigPath(configPath, directory) {
|
|
|
17018
17082
|
const normalizedUser = userConfigPath.replace(/\\/g, "/");
|
|
17019
17083
|
const normalizedProject = projectConfigPath.replace(/\\/g, "/");
|
|
17020
17084
|
try {
|
|
17021
|
-
const resolvedConfig =
|
|
17022
|
-
const resolvedUser =
|
|
17023
|
-
const resolvedProject =
|
|
17085
|
+
const resolvedConfig = path16.resolve(configPath);
|
|
17086
|
+
const resolvedUser = path16.resolve(normalizedUser);
|
|
17087
|
+
const resolvedProject = path16.resolve(normalizedProject);
|
|
17024
17088
|
return resolvedConfig === resolvedUser || resolvedConfig === resolvedProject;
|
|
17025
17089
|
} catch {
|
|
17026
17090
|
return false;
|
|
@@ -17060,12 +17124,12 @@ function createConfigBackup(directory) {
|
|
|
17060
17124
|
};
|
|
17061
17125
|
}
|
|
17062
17126
|
function writeBackupArtifact(directory, backup) {
|
|
17063
|
-
const swarmDir =
|
|
17127
|
+
const swarmDir = path16.join(directory, ".swarm");
|
|
17064
17128
|
if (!fs7.existsSync(swarmDir)) {
|
|
17065
17129
|
fs7.mkdirSync(swarmDir, { recursive: true });
|
|
17066
17130
|
}
|
|
17067
17131
|
const backupFilename = `config-backup-${backup.createdAt}.json`;
|
|
17068
|
-
const backupPath =
|
|
17132
|
+
const backupPath = path16.join(swarmDir, backupFilename);
|
|
17069
17133
|
const artifact = {
|
|
17070
17134
|
createdAt: backup.createdAt,
|
|
17071
17135
|
configPath: backup.configPath,
|
|
@@ -17095,7 +17159,7 @@ function restoreFromBackup(backupPath, directory) {
|
|
|
17095
17159
|
return null;
|
|
17096
17160
|
}
|
|
17097
17161
|
const targetPath = artifact.configPath;
|
|
17098
|
-
const targetDir =
|
|
17162
|
+
const targetDir = path16.dirname(targetPath);
|
|
17099
17163
|
if (!fs7.existsSync(targetDir)) {
|
|
17100
17164
|
fs7.mkdirSync(targetDir, { recursive: true });
|
|
17101
17165
|
}
|
|
@@ -17126,9 +17190,9 @@ function readConfigFromFile(directory) {
|
|
|
17126
17190
|
return null;
|
|
17127
17191
|
}
|
|
17128
17192
|
}
|
|
17129
|
-
function validateConfigKey(
|
|
17193
|
+
function validateConfigKey(path17, value, _config) {
|
|
17130
17194
|
const findings = [];
|
|
17131
|
-
switch (
|
|
17195
|
+
switch (path17) {
|
|
17132
17196
|
case "agents": {
|
|
17133
17197
|
if (value !== undefined) {
|
|
17134
17198
|
findings.push({
|
|
@@ -17375,27 +17439,27 @@ function validateConfigKey(path16, value, _config) {
|
|
|
17375
17439
|
}
|
|
17376
17440
|
return findings;
|
|
17377
17441
|
}
|
|
17378
|
-
function walkConfigAndValidate(obj,
|
|
17442
|
+
function walkConfigAndValidate(obj, path17, config3, findings) {
|
|
17379
17443
|
if (obj === null || obj === undefined) {
|
|
17380
17444
|
return;
|
|
17381
17445
|
}
|
|
17382
|
-
if (
|
|
17383
|
-
const keyFindings = validateConfigKey(
|
|
17446
|
+
if (path17 && typeof obj === "object" && !Array.isArray(obj)) {
|
|
17447
|
+
const keyFindings = validateConfigKey(path17, obj, config3);
|
|
17384
17448
|
findings.push(...keyFindings);
|
|
17385
17449
|
}
|
|
17386
17450
|
if (typeof obj !== "object") {
|
|
17387
|
-
const keyFindings = validateConfigKey(
|
|
17451
|
+
const keyFindings = validateConfigKey(path17, obj, config3);
|
|
17388
17452
|
findings.push(...keyFindings);
|
|
17389
17453
|
return;
|
|
17390
17454
|
}
|
|
17391
17455
|
if (Array.isArray(obj)) {
|
|
17392
17456
|
obj.forEach((item, index) => {
|
|
17393
|
-
walkConfigAndValidate(item, `${
|
|
17457
|
+
walkConfigAndValidate(item, `${path17}[${index}]`, config3, findings);
|
|
17394
17458
|
});
|
|
17395
17459
|
return;
|
|
17396
17460
|
}
|
|
17397
17461
|
for (const [key, value] of Object.entries(obj)) {
|
|
17398
|
-
const newPath =
|
|
17462
|
+
const newPath = path17 ? `${path17}.${key}` : key;
|
|
17399
17463
|
walkConfigAndValidate(value, newPath, config3, findings);
|
|
17400
17464
|
}
|
|
17401
17465
|
}
|
|
@@ -17515,7 +17579,7 @@ function applySafeAutoFixes(directory, result) {
|
|
|
17515
17579
|
}
|
|
17516
17580
|
}
|
|
17517
17581
|
if (appliedFixes.length > 0) {
|
|
17518
|
-
const configDir =
|
|
17582
|
+
const configDir = path16.dirname(configPath);
|
|
17519
17583
|
if (!fs7.existsSync(configDir)) {
|
|
17520
17584
|
fs7.mkdirSync(configDir, { recursive: true });
|
|
17521
17585
|
}
|
|
@@ -17525,12 +17589,12 @@ function applySafeAutoFixes(directory, result) {
|
|
|
17525
17589
|
return { appliedFixes, updatedConfigPath };
|
|
17526
17590
|
}
|
|
17527
17591
|
function writeDoctorArtifact(directory, result) {
|
|
17528
|
-
const swarmDir =
|
|
17592
|
+
const swarmDir = path16.join(directory, ".swarm");
|
|
17529
17593
|
if (!fs7.existsSync(swarmDir)) {
|
|
17530
17594
|
fs7.mkdirSync(swarmDir, { recursive: true });
|
|
17531
17595
|
}
|
|
17532
17596
|
const artifactFilename = "config-doctor.json";
|
|
17533
|
-
const artifactPath =
|
|
17597
|
+
const artifactPath = path16.join(swarmDir, artifactFilename);
|
|
17534
17598
|
const guiOutput = {
|
|
17535
17599
|
timestamp: result.timestamp,
|
|
17536
17600
|
summary: result.summary,
|
|
@@ -17893,7 +17957,7 @@ var init_evidence_summary_service = __esm(() => {
|
|
|
17893
17957
|
// src/cli/index.ts
|
|
17894
17958
|
import * as fs18 from "fs";
|
|
17895
17959
|
import * as os6 from "os";
|
|
17896
|
-
import * as
|
|
17960
|
+
import * as path28 from "path";
|
|
17897
17961
|
|
|
17898
17962
|
// src/commands/agents.ts
|
|
17899
17963
|
function handleAgentsCommand(agents, guardrails) {
|
|
@@ -32005,7 +32069,9 @@ async function handleClarifyCommand(_directory, args) {
|
|
|
32005
32069
|
}
|
|
32006
32070
|
|
|
32007
32071
|
// src/commands/close.ts
|
|
32072
|
+
import { execFileSync } from "child_process";
|
|
32008
32073
|
import { promises as fs6 } from "fs";
|
|
32074
|
+
import path11 from "path";
|
|
32009
32075
|
init_manager();
|
|
32010
32076
|
|
|
32011
32077
|
// src/hooks/knowledge-store.ts
|
|
@@ -33117,87 +33183,183 @@ var write_retro = createSwarmTool({
|
|
|
33117
33183
|
});
|
|
33118
33184
|
|
|
33119
33185
|
// src/commands/close.ts
|
|
33120
|
-
async function handleCloseCommand(directory,
|
|
33186
|
+
async function handleCloseCommand(directory, args) {
|
|
33121
33187
|
const planPath = validateSwarmPath(directory, "plan.json");
|
|
33122
|
-
let
|
|
33188
|
+
let planExists = false;
|
|
33189
|
+
let planData = {
|
|
33190
|
+
title: path11.basename(directory) || "Ad-hoc session",
|
|
33191
|
+
phases: []
|
|
33192
|
+
};
|
|
33123
33193
|
try {
|
|
33124
33194
|
const content = await fs6.readFile(planPath, "utf-8");
|
|
33125
33195
|
planData = JSON.parse(content);
|
|
33196
|
+
planExists = true;
|
|
33126
33197
|
} catch (error93) {
|
|
33127
|
-
|
|
33198
|
+
if (error93?.code !== "ENOENT") {
|
|
33199
|
+
return `\u274C Failed to read plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
33200
|
+
}
|
|
33201
|
+
const swarmDirExists = await fs6.access(path11.join(directory, ".swarm")).then(() => true).catch(() => false);
|
|
33202
|
+
if (!swarmDirExists) {
|
|
33203
|
+
return `\u274C No .swarm/ directory found in ${directory}. Run /swarm close from the project root, or run /swarm plan first.`;
|
|
33204
|
+
}
|
|
33128
33205
|
}
|
|
33129
33206
|
const phases = planData.phases ?? [];
|
|
33130
33207
|
const inProgressPhases = phases.filter((p) => p.status === "in_progress");
|
|
33131
|
-
|
|
33132
|
-
if (
|
|
33133
|
-
|
|
33134
|
-
const blockedCount = phases.filter((p) => p.status === "blocked").length;
|
|
33135
|
-
const completeCount = phases.filter((p) => p.status === "complete" || p.status === "completed").length;
|
|
33136
|
-
return `\u2139\uFE0F Swarm already closed. ${completeCount} phases complete, ${closedCount} phases closed, ${blockedCount} phases blocked. No action taken.`;
|
|
33208
|
+
let planAlreadyDone = false;
|
|
33209
|
+
if (planExists) {
|
|
33210
|
+
planAlreadyDone = phases.length > 0 && phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
|
|
33137
33211
|
}
|
|
33138
33212
|
const config3 = KnowledgeConfigSchema.parse({});
|
|
33139
33213
|
const projectName = planData.title ?? "Unknown Project";
|
|
33140
33214
|
const closedPhases = [];
|
|
33141
33215
|
const closedTasks = [];
|
|
33142
33216
|
const warnings = [];
|
|
33143
|
-
|
|
33144
|
-
|
|
33145
|
-
|
|
33146
|
-
|
|
33147
|
-
|
|
33148
|
-
|
|
33149
|
-
|
|
33150
|
-
|
|
33151
|
-
|
|
33152
|
-
|
|
33153
|
-
|
|
33154
|
-
|
|
33155
|
-
|
|
33156
|
-
|
|
33157
|
-
|
|
33158
|
-
|
|
33159
|
-
|
|
33160
|
-
|
|
33217
|
+
if (!planAlreadyDone) {
|
|
33218
|
+
for (const phase of inProgressPhases) {
|
|
33219
|
+
closedPhases.push(phase.id);
|
|
33220
|
+
let retroResult;
|
|
33221
|
+
try {
|
|
33222
|
+
retroResult = await executeWriteRetro({
|
|
33223
|
+
phase: phase.id,
|
|
33224
|
+
summary: "Phase closed via /swarm close",
|
|
33225
|
+
task_count: Math.max(1, (phase.tasks ?? []).length),
|
|
33226
|
+
task_complexity: "simple",
|
|
33227
|
+
total_tool_calls: 0,
|
|
33228
|
+
coder_revisions: 0,
|
|
33229
|
+
reviewer_rejections: 0,
|
|
33230
|
+
test_failures: 0,
|
|
33231
|
+
security_findings: 0,
|
|
33232
|
+
integration_issues: 0
|
|
33233
|
+
}, directory);
|
|
33234
|
+
} catch (retroError) {
|
|
33235
|
+
warnings.push(`Retrospective write threw for phase ${phase.id}: ${retroError instanceof Error ? retroError.message : String(retroError)}`);
|
|
33236
|
+
}
|
|
33237
|
+
if (retroResult !== undefined) {
|
|
33238
|
+
try {
|
|
33239
|
+
const parsed = JSON.parse(retroResult);
|
|
33240
|
+
if (parsed.success !== true) {
|
|
33241
|
+
warnings.push(`Retrospective write failed for phase ${phase.id}`);
|
|
33242
|
+
}
|
|
33243
|
+
} catch {}
|
|
33161
33244
|
}
|
|
33162
|
-
|
|
33163
|
-
|
|
33164
|
-
|
|
33165
|
-
|
|
33245
|
+
for (const task of phase.tasks ?? []) {
|
|
33246
|
+
if (task.status !== "completed" && task.status !== "complete") {
|
|
33247
|
+
closedTasks.push(task.id);
|
|
33248
|
+
}
|
|
33166
33249
|
}
|
|
33167
33250
|
}
|
|
33168
33251
|
}
|
|
33252
|
+
const lessonsFilePath = path11.join(directory, ".swarm", "close-lessons.md");
|
|
33253
|
+
let explicitLessons = [];
|
|
33254
|
+
try {
|
|
33255
|
+
const lessonsText = await fs6.readFile(lessonsFilePath, "utf-8");
|
|
33256
|
+
explicitLessons = lessonsText.split(`
|
|
33257
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
33258
|
+
} catch {}
|
|
33259
|
+
let curationSucceeded = false;
|
|
33169
33260
|
try {
|
|
33170
|
-
await curateAndStoreSwarm(
|
|
33261
|
+
await curateAndStoreSwarm(explicitLessons, projectName, { phase_number: 0 }, directory, config3);
|
|
33262
|
+
curationSucceeded = true;
|
|
33171
33263
|
} catch (error93) {
|
|
33172
33264
|
console.warn("[close-command] curateAndStoreSwarm error:", error93);
|
|
33173
33265
|
}
|
|
33174
|
-
|
|
33175
|
-
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
33266
|
+
if (curationSucceeded && explicitLessons.length > 0) {
|
|
33267
|
+
await fs6.unlink(lessonsFilePath).catch(() => {});
|
|
33268
|
+
}
|
|
33269
|
+
if (planExists && !planAlreadyDone) {
|
|
33270
|
+
for (const phase of phases) {
|
|
33271
|
+
if (phase.status !== "complete" && phase.status !== "completed") {
|
|
33272
|
+
phase.status = "closed";
|
|
33273
|
+
if (!closedPhases.includes(phase.id)) {
|
|
33274
|
+
closedPhases.push(phase.id);
|
|
33275
|
+
}
|
|
33179
33276
|
}
|
|
33180
|
-
|
|
33181
|
-
|
|
33182
|
-
|
|
33183
|
-
|
|
33184
|
-
|
|
33185
|
-
|
|
33277
|
+
for (const task of phase.tasks ?? []) {
|
|
33278
|
+
if (task.status !== "completed" && task.status !== "complete") {
|
|
33279
|
+
task.status = "closed";
|
|
33280
|
+
if (!closedTasks.includes(task.id)) {
|
|
33281
|
+
closedTasks.push(task.id);
|
|
33282
|
+
}
|
|
33186
33283
|
}
|
|
33187
33284
|
}
|
|
33188
33285
|
}
|
|
33286
|
+
try {
|
|
33287
|
+
await fs6.writeFile(planPath, JSON.stringify(planData, null, 2), "utf-8");
|
|
33288
|
+
} catch (error93) {
|
|
33289
|
+
console.warn("[close-command] Failed to write plan.json:", error93);
|
|
33290
|
+
}
|
|
33189
33291
|
}
|
|
33190
33292
|
try {
|
|
33191
|
-
await
|
|
33293
|
+
await archiveEvidence(directory, 30, 10);
|
|
33192
33294
|
} catch (error93) {
|
|
33193
|
-
console.warn("[close-command]
|
|
33295
|
+
console.warn("[close-command] archiveEvidence error:", error93);
|
|
33194
33296
|
}
|
|
33297
|
+
const swarmDir = path11.join(directory, ".swarm");
|
|
33298
|
+
let configBackupsRemoved = 0;
|
|
33195
33299
|
try {
|
|
33196
|
-
await
|
|
33300
|
+
const swarmFiles = await fs6.readdir(swarmDir);
|
|
33301
|
+
const configBackups = swarmFiles.filter((f) => f.startsWith("config-backup-") && f.endsWith(".json"));
|
|
33302
|
+
for (const backup of configBackups) {
|
|
33303
|
+
try {
|
|
33304
|
+
await fs6.unlink(path11.join(swarmDir, backup));
|
|
33305
|
+
configBackupsRemoved++;
|
|
33306
|
+
} catch {}
|
|
33307
|
+
}
|
|
33308
|
+
} catch {}
|
|
33309
|
+
const contextPath = path11.join(directory, ".swarm", "context.md");
|
|
33310
|
+
const contextContent = [
|
|
33311
|
+
"# Context",
|
|
33312
|
+
"",
|
|
33313
|
+
"## Status",
|
|
33314
|
+
`Session closed after: ${projectName}`,
|
|
33315
|
+
`Closed: ${new Date().toISOString()}`,
|
|
33316
|
+
"No active plan. Next session starts fresh.",
|
|
33317
|
+
""
|
|
33318
|
+
].join(`
|
|
33319
|
+
`);
|
|
33320
|
+
try {
|
|
33321
|
+
await fs6.writeFile(contextPath, contextContent, "utf-8");
|
|
33197
33322
|
} catch (error93) {
|
|
33198
|
-
console.warn("[close-command]
|
|
33323
|
+
console.warn("[close-command] Failed to write context.md:", error93);
|
|
33324
|
+
}
|
|
33325
|
+
const pruneBranches = args.includes("--prune-branches");
|
|
33326
|
+
const prunedBranches = [];
|
|
33327
|
+
const pruneErrors = [];
|
|
33328
|
+
if (pruneBranches) {
|
|
33329
|
+
try {
|
|
33330
|
+
const branchOutput = execFileSync("git", ["branch", "-vv"], {
|
|
33331
|
+
cwd: directory,
|
|
33332
|
+
encoding: "utf-8",
|
|
33333
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
33334
|
+
});
|
|
33335
|
+
const goneBranches = branchOutput.split(`
|
|
33336
|
+
`).filter((line) => line.includes(": gone]")).map((line) => line.trim().replace(/^[*+]\s+/, "").split(/\s+/)[0]).filter(Boolean);
|
|
33337
|
+
for (const branch of goneBranches) {
|
|
33338
|
+
try {
|
|
33339
|
+
execFileSync("git", ["branch", "-d", branch], {
|
|
33340
|
+
cwd: directory,
|
|
33341
|
+
encoding: "utf-8",
|
|
33342
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
33343
|
+
});
|
|
33344
|
+
prunedBranches.push(branch);
|
|
33345
|
+
} catch {
|
|
33346
|
+
pruneErrors.push(branch);
|
|
33347
|
+
}
|
|
33348
|
+
}
|
|
33349
|
+
} catch {}
|
|
33199
33350
|
}
|
|
33200
33351
|
const closeSummaryPath = validateSwarmPath(directory, "close-summary.md");
|
|
33352
|
+
const actionsPerformed = [
|
|
33353
|
+
...!planAlreadyDone && inProgressPhases.length > 0 ? ["- Wrote retrospectives for in-progress phases"] : [],
|
|
33354
|
+
"- Archived evidence bundles",
|
|
33355
|
+
"- Reset context.md for next session",
|
|
33356
|
+
...configBackupsRemoved > 0 ? [`- Removed ${configBackupsRemoved} stale config backup file(s)`] : [],
|
|
33357
|
+
...prunedBranches.length > 0 ? [
|
|
33358
|
+
`- Pruned ${prunedBranches.length} stale local git branch(es): ${prunedBranches.join(", ")}`
|
|
33359
|
+
] : [],
|
|
33360
|
+
"- Cleared agent sessions and delegation chains",
|
|
33361
|
+
...planExists && !planAlreadyDone ? ["- Set non-completed phases/tasks to closed status"] : []
|
|
33362
|
+
];
|
|
33201
33363
|
const summaryContent = [
|
|
33202
33364
|
"# Swarm Close Summary",
|
|
33203
33365
|
"",
|
|
@@ -33205,18 +33367,15 @@ async function handleCloseCommand(directory, _args) {
|
|
|
33205
33367
|
`**Closed:** ${new Date().toISOString()}`,
|
|
33206
33368
|
"",
|
|
33207
33369
|
`## Phases Closed: ${closedPhases.length}`,
|
|
33208
|
-
closedPhases.map((id) => `- Phase ${id}`).join(`
|
|
33209
|
-
`),
|
|
33370
|
+
!planExists ? "_No plan \u2014 ad-hoc session_" : closedPhases.length > 0 ? closedPhases.map((id) => `- Phase ${id}`).join(`
|
|
33371
|
+
`) : "_No phases to close_",
|
|
33210
33372
|
"",
|
|
33211
33373
|
`## Tasks Closed: ${closedTasks.length}`,
|
|
33212
33374
|
closedTasks.length > 0 ? closedTasks.map((id) => `- ${id}`).join(`
|
|
33213
33375
|
`) : "_No incomplete tasks_",
|
|
33214
33376
|
"",
|
|
33215
33377
|
"## Actions Performed",
|
|
33216
|
-
|
|
33217
|
-
"- Archived evidence bundles",
|
|
33218
|
-
"- Cleared agent sessions and delegation chains",
|
|
33219
|
-
"- Set non-completed phases/tasks to closed status"
|
|
33378
|
+
...actionsPerformed
|
|
33220
33379
|
].join(`
|
|
33221
33380
|
`);
|
|
33222
33381
|
try {
|
|
@@ -33232,20 +33391,26 @@ async function handleCloseCommand(directory, _args) {
|
|
|
33232
33391
|
await writeCheckpoint(directory).catch(() => {});
|
|
33233
33392
|
swarmState.agentSessions.clear();
|
|
33234
33393
|
swarmState.delegationChains.clear();
|
|
33394
|
+
if (pruneErrors.length > 0) {
|
|
33395
|
+
warnings.push(`Could not prune ${pruneErrors.length} branch(es) (unmerged or checked out): ${pruneErrors.join(", ")}`);
|
|
33396
|
+
}
|
|
33235
33397
|
const warningMsg = warnings.length > 0 ? ` Warnings: ${warnings.join("; ")}.` : "";
|
|
33398
|
+
if (planAlreadyDone) {
|
|
33399
|
+
return `\u2705 Session closed. Plan was already in a terminal state \u2014 cleanup steps applied.${warningMsg}`;
|
|
33400
|
+
}
|
|
33236
33401
|
return `\u2705 Swarm closed successfully. ${closedPhases.length} phase(s) closed, ${closedTasks.length} incomplete task(s) marked closed.${warningMsg}`;
|
|
33237
33402
|
}
|
|
33238
33403
|
|
|
33239
33404
|
// src/commands/config.ts
|
|
33240
33405
|
import * as os3 from "os";
|
|
33241
|
-
import * as
|
|
33406
|
+
import * as path12 from "path";
|
|
33242
33407
|
function getUserConfigDir2() {
|
|
33243
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
33408
|
+
return process.env.XDG_CONFIG_HOME || path12.join(os3.homedir(), ".config");
|
|
33244
33409
|
}
|
|
33245
33410
|
async function handleConfigCommand(directory, _args) {
|
|
33246
33411
|
const config3 = loadPluginConfig(directory);
|
|
33247
|
-
const userConfigPath =
|
|
33248
|
-
const projectConfigPath =
|
|
33412
|
+
const userConfigPath = path12.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
|
|
33413
|
+
const projectConfigPath = path12.join(directory, ".opencode", "opencode-swarm.json");
|
|
33249
33414
|
const lines = [
|
|
33250
33415
|
"## Swarm Configuration",
|
|
33251
33416
|
"",
|
|
@@ -33513,13 +33678,13 @@ function formatCurationSummary(summary) {
|
|
|
33513
33678
|
}
|
|
33514
33679
|
|
|
33515
33680
|
// src/commands/dark-matter.ts
|
|
33516
|
-
import
|
|
33681
|
+
import path14 from "path";
|
|
33517
33682
|
|
|
33518
33683
|
// src/tools/co-change-analyzer.ts
|
|
33519
33684
|
import * as child_process2 from "child_process";
|
|
33520
33685
|
import { randomUUID } from "crypto";
|
|
33521
33686
|
import { readdir, readFile as readFile2, stat } from "fs/promises";
|
|
33522
|
-
import * as
|
|
33687
|
+
import * as path13 from "path";
|
|
33523
33688
|
import { promisify } from "util";
|
|
33524
33689
|
function getExecFileAsync() {
|
|
33525
33690
|
return promisify(child_process2.execFile);
|
|
@@ -33621,7 +33786,7 @@ async function scanSourceFiles(dir) {
|
|
|
33621
33786
|
try {
|
|
33622
33787
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
33623
33788
|
for (const entry of entries) {
|
|
33624
|
-
const fullPath =
|
|
33789
|
+
const fullPath = path13.join(dir, entry.name);
|
|
33625
33790
|
if (entry.isDirectory()) {
|
|
33626
33791
|
if (skipDirs.has(entry.name)) {
|
|
33627
33792
|
continue;
|
|
@@ -33629,7 +33794,7 @@ async function scanSourceFiles(dir) {
|
|
|
33629
33794
|
const subFiles = await scanSourceFiles(fullPath);
|
|
33630
33795
|
results.push(...subFiles);
|
|
33631
33796
|
} else if (entry.isFile()) {
|
|
33632
|
-
const ext =
|
|
33797
|
+
const ext = path13.extname(entry.name);
|
|
33633
33798
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
|
|
33634
33799
|
results.push(fullPath);
|
|
33635
33800
|
}
|
|
@@ -33651,8 +33816,8 @@ async function getStaticEdges(directory) {
|
|
|
33651
33816
|
continue;
|
|
33652
33817
|
}
|
|
33653
33818
|
try {
|
|
33654
|
-
const sourceDir =
|
|
33655
|
-
const resolvedPath =
|
|
33819
|
+
const sourceDir = path13.dirname(sourceFile);
|
|
33820
|
+
const resolvedPath = path13.resolve(sourceDir, importPath);
|
|
33656
33821
|
const extensions = [
|
|
33657
33822
|
"",
|
|
33658
33823
|
".ts",
|
|
@@ -33677,8 +33842,8 @@ async function getStaticEdges(directory) {
|
|
|
33677
33842
|
if (!targetFile) {
|
|
33678
33843
|
continue;
|
|
33679
33844
|
}
|
|
33680
|
-
const relSource =
|
|
33681
|
-
const relTarget =
|
|
33845
|
+
const relSource = path13.relative(directory, sourceFile).replace(/\\/g, "/");
|
|
33846
|
+
const relTarget = path13.relative(directory, targetFile).replace(/\\/g, "/");
|
|
33682
33847
|
const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
|
|
33683
33848
|
edges.add(key);
|
|
33684
33849
|
} catch {}
|
|
@@ -33690,7 +33855,7 @@ async function getStaticEdges(directory) {
|
|
|
33690
33855
|
function isTestImplementationPair(fileA, fileB) {
|
|
33691
33856
|
const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
|
|
33692
33857
|
const getBaseName = (filePath) => {
|
|
33693
|
-
const base =
|
|
33858
|
+
const base = path13.basename(filePath);
|
|
33694
33859
|
for (const pattern of testPatterns) {
|
|
33695
33860
|
if (base.endsWith(pattern)) {
|
|
33696
33861
|
return base.slice(0, -pattern.length);
|
|
@@ -33700,16 +33865,16 @@ function isTestImplementationPair(fileA, fileB) {
|
|
|
33700
33865
|
};
|
|
33701
33866
|
const baseA = getBaseName(fileA);
|
|
33702
33867
|
const baseB = getBaseName(fileB);
|
|
33703
|
-
return baseA === baseB && baseA !==
|
|
33868
|
+
return baseA === baseB && baseA !== path13.basename(fileA) && baseA !== path13.basename(fileB);
|
|
33704
33869
|
}
|
|
33705
33870
|
function hasSharedPrefix(fileA, fileB) {
|
|
33706
|
-
const dirA =
|
|
33707
|
-
const dirB =
|
|
33871
|
+
const dirA = path13.dirname(fileA);
|
|
33872
|
+
const dirB = path13.dirname(fileB);
|
|
33708
33873
|
if (dirA !== dirB) {
|
|
33709
33874
|
return false;
|
|
33710
33875
|
}
|
|
33711
|
-
const baseA =
|
|
33712
|
-
const baseB =
|
|
33876
|
+
const baseA = path13.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
33877
|
+
const baseB = path13.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
33713
33878
|
if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
|
|
33714
33879
|
return true;
|
|
33715
33880
|
}
|
|
@@ -33763,8 +33928,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
|
|
|
33763
33928
|
const entries = [];
|
|
33764
33929
|
const now = new Date().toISOString();
|
|
33765
33930
|
for (const pair of pairs.slice(0, 10)) {
|
|
33766
|
-
const baseA =
|
|
33767
|
-
const baseB =
|
|
33931
|
+
const baseA = path13.basename(pair.fileA);
|
|
33932
|
+
const baseB = path13.basename(pair.fileB);
|
|
33768
33933
|
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.`;
|
|
33769
33934
|
if (lesson.length > 280) {
|
|
33770
33935
|
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.`;
|
|
@@ -33874,7 +34039,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33874
34039
|
const output = formatDarkMatterOutput(pairs);
|
|
33875
34040
|
if (pairs.length > 0) {
|
|
33876
34041
|
try {
|
|
33877
|
-
const projectName =
|
|
34042
|
+
const projectName = path14.basename(path14.resolve(directory));
|
|
33878
34043
|
const entries = darkMatterToKnowledgeEntries(pairs, projectName);
|
|
33879
34044
|
if (entries.length > 0) {
|
|
33880
34045
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
@@ -33895,8 +34060,8 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33895
34060
|
|
|
33896
34061
|
// src/services/diagnose-service.ts
|
|
33897
34062
|
import * as child_process3 from "child_process";
|
|
33898
|
-
import { existsSync as
|
|
33899
|
-
import
|
|
34063
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
34064
|
+
import path15 from "path";
|
|
33900
34065
|
import { fileURLToPath } from "url";
|
|
33901
34066
|
init_manager();
|
|
33902
34067
|
init_utils2();
|
|
@@ -34132,7 +34297,7 @@ async function checkConfigBackups(directory) {
|
|
|
34132
34297
|
}
|
|
34133
34298
|
async function checkGitRepository(directory) {
|
|
34134
34299
|
try {
|
|
34135
|
-
if (!
|
|
34300
|
+
if (!existsSync6(directory) || !statSync3(directory).isDirectory()) {
|
|
34136
34301
|
return {
|
|
34137
34302
|
name: "Git Repository",
|
|
34138
34303
|
status: "\u274C",
|
|
@@ -34196,8 +34361,8 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
34196
34361
|
};
|
|
34197
34362
|
}
|
|
34198
34363
|
async function checkConfigParseability(directory) {
|
|
34199
|
-
const configPath =
|
|
34200
|
-
if (!
|
|
34364
|
+
const configPath = path15.join(directory, ".opencode/opencode-swarm.json");
|
|
34365
|
+
if (!existsSync6(configPath)) {
|
|
34201
34366
|
return {
|
|
34202
34367
|
name: "Config Parseability",
|
|
34203
34368
|
status: "\u2705",
|
|
@@ -34243,15 +34408,15 @@ async function checkGrammarWasmFiles() {
|
|
|
34243
34408
|
"tree-sitter-ini.wasm",
|
|
34244
34409
|
"tree-sitter-regex.wasm"
|
|
34245
34410
|
];
|
|
34246
|
-
const thisDir =
|
|
34411
|
+
const thisDir = path15.dirname(fileURLToPath(import.meta.url));
|
|
34247
34412
|
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
34248
|
-
const grammarDir = isSource ?
|
|
34413
|
+
const grammarDir = isSource ? path15.join(thisDir, "..", "lang", "grammars") : path15.join(thisDir, "lang", "grammars");
|
|
34249
34414
|
const missing = [];
|
|
34250
|
-
if (!
|
|
34415
|
+
if (!existsSync6(path15.join(grammarDir, "tree-sitter.wasm"))) {
|
|
34251
34416
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
34252
34417
|
}
|
|
34253
34418
|
for (const file3 of grammarFiles) {
|
|
34254
|
-
if (!
|
|
34419
|
+
if (!existsSync6(path15.join(grammarDir, file3))) {
|
|
34255
34420
|
missing.push(file3);
|
|
34256
34421
|
}
|
|
34257
34422
|
}
|
|
@@ -34269,8 +34434,8 @@ async function checkGrammarWasmFiles() {
|
|
|
34269
34434
|
};
|
|
34270
34435
|
}
|
|
34271
34436
|
async function checkCheckpointManifest(directory) {
|
|
34272
|
-
const manifestPath =
|
|
34273
|
-
if (!
|
|
34437
|
+
const manifestPath = path15.join(directory, ".swarm/checkpoints.json");
|
|
34438
|
+
if (!existsSync6(manifestPath)) {
|
|
34274
34439
|
return {
|
|
34275
34440
|
name: "Checkpoint Manifest",
|
|
34276
34441
|
status: "\u2705",
|
|
@@ -34321,8 +34486,8 @@ async function checkCheckpointManifest(directory) {
|
|
|
34321
34486
|
}
|
|
34322
34487
|
}
|
|
34323
34488
|
async function checkEventStreamIntegrity(directory) {
|
|
34324
|
-
const eventsPath =
|
|
34325
|
-
if (!
|
|
34489
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34490
|
+
if (!existsSync6(eventsPath)) {
|
|
34326
34491
|
return {
|
|
34327
34492
|
name: "Event Stream",
|
|
34328
34493
|
status: "\u2705",
|
|
@@ -34362,8 +34527,8 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
34362
34527
|
}
|
|
34363
34528
|
}
|
|
34364
34529
|
async function checkSteeringDirectives(directory) {
|
|
34365
|
-
const eventsPath =
|
|
34366
|
-
if (!
|
|
34530
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34531
|
+
if (!existsSync6(eventsPath)) {
|
|
34367
34532
|
return {
|
|
34368
34533
|
name: "Steering Directives",
|
|
34369
34534
|
status: "\u2705",
|
|
@@ -34418,8 +34583,8 @@ async function checkCurator(directory) {
|
|
|
34418
34583
|
detail: "Disabled (enable via curator.enabled)"
|
|
34419
34584
|
};
|
|
34420
34585
|
}
|
|
34421
|
-
const summaryPath =
|
|
34422
|
-
if (!
|
|
34586
|
+
const summaryPath = path15.join(directory, ".swarm/curator-summary.json");
|
|
34587
|
+
if (!existsSync6(summaryPath)) {
|
|
34423
34588
|
return {
|
|
34424
34589
|
name: "Curator",
|
|
34425
34590
|
status: "\u2705",
|
|
@@ -35313,14 +35478,14 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
35313
35478
|
}
|
|
35314
35479
|
// src/hooks/knowledge-migrator.ts
|
|
35315
35480
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
35316
|
-
import { existsSync as
|
|
35481
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
35317
35482
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
35318
|
-
import * as
|
|
35483
|
+
import * as path17 from "path";
|
|
35319
35484
|
async function migrateContextToKnowledge(directory, config3) {
|
|
35320
|
-
const sentinelPath =
|
|
35321
|
-
const contextPath =
|
|
35485
|
+
const sentinelPath = path17.join(directory, ".swarm", ".knowledge-migrated");
|
|
35486
|
+
const contextPath = path17.join(directory, ".swarm", "context.md");
|
|
35322
35487
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
35323
|
-
if (
|
|
35488
|
+
if (existsSync8(sentinelPath)) {
|
|
35324
35489
|
return {
|
|
35325
35490
|
migrated: false,
|
|
35326
35491
|
entriesMigrated: 0,
|
|
@@ -35329,7 +35494,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
35329
35494
|
skippedReason: "sentinel-exists"
|
|
35330
35495
|
};
|
|
35331
35496
|
}
|
|
35332
|
-
if (!
|
|
35497
|
+
if (!existsSync8(contextPath)) {
|
|
35333
35498
|
return {
|
|
35334
35499
|
migrated: false,
|
|
35335
35500
|
entriesMigrated: 0,
|
|
@@ -35514,8 +35679,8 @@ function truncateLesson(text) {
|
|
|
35514
35679
|
return `${text.slice(0, 277)}...`;
|
|
35515
35680
|
}
|
|
35516
35681
|
function inferProjectName(directory) {
|
|
35517
|
-
const packageJsonPath =
|
|
35518
|
-
if (
|
|
35682
|
+
const packageJsonPath = path17.join(directory, "package.json");
|
|
35683
|
+
if (existsSync8(packageJsonPath)) {
|
|
35519
35684
|
try {
|
|
35520
35685
|
const pkg = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
|
|
35521
35686
|
if (pkg.name && typeof pkg.name === "string") {
|
|
@@ -35523,7 +35688,7 @@ function inferProjectName(directory) {
|
|
|
35523
35688
|
}
|
|
35524
35689
|
} catch {}
|
|
35525
35690
|
}
|
|
35526
|
-
return
|
|
35691
|
+
return path17.basename(directory);
|
|
35527
35692
|
}
|
|
35528
35693
|
async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
35529
35694
|
const sentinel = {
|
|
@@ -35535,7 +35700,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
35535
35700
|
schema_version: 1,
|
|
35536
35701
|
migration_tool: "knowledge-migrator.ts"
|
|
35537
35702
|
};
|
|
35538
|
-
await mkdir3(
|
|
35703
|
+
await mkdir3(path17.dirname(sentinelPath), { recursive: true });
|
|
35539
35704
|
await writeFile3(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
35540
35705
|
}
|
|
35541
35706
|
|
|
@@ -35772,15 +35937,15 @@ async function handlePlanCommand(directory, args) {
|
|
|
35772
35937
|
init_manager();
|
|
35773
35938
|
init_manager2();
|
|
35774
35939
|
import * as fs13 from "fs";
|
|
35775
|
-
import * as
|
|
35940
|
+
import * as path23 from "path";
|
|
35776
35941
|
|
|
35777
35942
|
// src/tools/lint.ts
|
|
35778
35943
|
import * as fs9 from "fs";
|
|
35779
|
-
import * as
|
|
35944
|
+
import * as path19 from "path";
|
|
35780
35945
|
|
|
35781
35946
|
// src/build/discovery.ts
|
|
35782
35947
|
import * as fs8 from "fs";
|
|
35783
|
-
import * as
|
|
35948
|
+
import * as path18 from "path";
|
|
35784
35949
|
|
|
35785
35950
|
// src/lang/detector.ts
|
|
35786
35951
|
import { access, readdir as readdir2 } from "fs/promises";
|
|
@@ -36877,11 +37042,11 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36877
37042
|
const regex = simpleGlobToRegex(pattern);
|
|
36878
37043
|
const matches = files.filter((f) => regex.test(f));
|
|
36879
37044
|
if (matches.length > 0) {
|
|
36880
|
-
return
|
|
37045
|
+
return path18.join(dir, matches[0]);
|
|
36881
37046
|
}
|
|
36882
37047
|
} catch {}
|
|
36883
37048
|
} else {
|
|
36884
|
-
const filePath =
|
|
37049
|
+
const filePath = path18.join(workingDir, pattern);
|
|
36885
37050
|
if (fs8.existsSync(filePath)) {
|
|
36886
37051
|
return filePath;
|
|
36887
37052
|
}
|
|
@@ -36890,7 +37055,7 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36890
37055
|
return null;
|
|
36891
37056
|
}
|
|
36892
37057
|
function getRepoDefinedScripts(workingDir, scripts) {
|
|
36893
|
-
const packageJsonPath =
|
|
37058
|
+
const packageJsonPath = path18.join(workingDir, "package.json");
|
|
36894
37059
|
if (!fs8.existsSync(packageJsonPath)) {
|
|
36895
37060
|
return [];
|
|
36896
37061
|
}
|
|
@@ -36931,7 +37096,7 @@ function findAllBuildFiles(workingDir) {
|
|
|
36931
37096
|
const regex = simpleGlobToRegex(pattern);
|
|
36932
37097
|
findFilesRecursive(workingDir, regex, allBuildFiles);
|
|
36933
37098
|
} else {
|
|
36934
|
-
const filePath =
|
|
37099
|
+
const filePath = path18.join(workingDir, pattern);
|
|
36935
37100
|
if (fs8.existsSync(filePath)) {
|
|
36936
37101
|
allBuildFiles.add(filePath);
|
|
36937
37102
|
}
|
|
@@ -36944,7 +37109,7 @@ function findFilesRecursive(dir, regex, results) {
|
|
|
36944
37109
|
try {
|
|
36945
37110
|
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
36946
37111
|
for (const entry of entries) {
|
|
36947
|
-
const fullPath =
|
|
37112
|
+
const fullPath = path18.join(dir, entry.name);
|
|
36948
37113
|
if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
|
|
36949
37114
|
findFilesRecursive(fullPath, regex, results);
|
|
36950
37115
|
} else if (entry.isFile() && regex.test(entry.name)) {
|
|
@@ -36967,7 +37132,7 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
|
|
|
36967
37132
|
let foundCommand = false;
|
|
36968
37133
|
for (const cmd of sortedCommands) {
|
|
36969
37134
|
if (cmd.detectFile) {
|
|
36970
|
-
const detectFilePath =
|
|
37135
|
+
const detectFilePath = path18.join(workingDir, cmd.detectFile);
|
|
36971
37136
|
if (!fs8.existsSync(detectFilePath)) {
|
|
36972
37137
|
continue;
|
|
36973
37138
|
}
|
|
@@ -37115,9 +37280,9 @@ function validateArgs(args) {
|
|
|
37115
37280
|
}
|
|
37116
37281
|
function getLinterCommand(linter, mode, projectDir) {
|
|
37117
37282
|
const isWindows = process.platform === "win32";
|
|
37118
|
-
const binDir =
|
|
37119
|
-
const biomeBin = isWindows ?
|
|
37120
|
-
const eslintBin = isWindows ?
|
|
37283
|
+
const binDir = path19.join(projectDir, "node_modules", ".bin");
|
|
37284
|
+
const biomeBin = isWindows ? path19.join(binDir, "biome.EXE") : path19.join(binDir, "biome");
|
|
37285
|
+
const eslintBin = isWindows ? path19.join(binDir, "eslint.cmd") : path19.join(binDir, "eslint");
|
|
37121
37286
|
switch (linter) {
|
|
37122
37287
|
case "biome":
|
|
37123
37288
|
if (mode === "fix") {
|
|
@@ -37133,7 +37298,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
37133
37298
|
}
|
|
37134
37299
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
37135
37300
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
37136
|
-
const gradlew = fs9.existsSync(
|
|
37301
|
+
const gradlew = fs9.existsSync(path19.join(cwd, gradlewName)) ? path19.join(cwd, gradlewName) : null;
|
|
37137
37302
|
switch (linter) {
|
|
37138
37303
|
case "ruff":
|
|
37139
37304
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -37167,10 +37332,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
37167
37332
|
}
|
|
37168
37333
|
}
|
|
37169
37334
|
function detectRuff(cwd) {
|
|
37170
|
-
if (fs9.existsSync(
|
|
37335
|
+
if (fs9.existsSync(path19.join(cwd, "ruff.toml")))
|
|
37171
37336
|
return isCommandAvailable("ruff");
|
|
37172
37337
|
try {
|
|
37173
|
-
const pyproject =
|
|
37338
|
+
const pyproject = path19.join(cwd, "pyproject.toml");
|
|
37174
37339
|
if (fs9.existsSync(pyproject)) {
|
|
37175
37340
|
const content = fs9.readFileSync(pyproject, "utf-8");
|
|
37176
37341
|
if (content.includes("[tool.ruff]"))
|
|
@@ -37180,19 +37345,19 @@ function detectRuff(cwd) {
|
|
|
37180
37345
|
return false;
|
|
37181
37346
|
}
|
|
37182
37347
|
function detectClippy(cwd) {
|
|
37183
|
-
return fs9.existsSync(
|
|
37348
|
+
return fs9.existsSync(path19.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
37184
37349
|
}
|
|
37185
37350
|
function detectGolangciLint(cwd) {
|
|
37186
|
-
return fs9.existsSync(
|
|
37351
|
+
return fs9.existsSync(path19.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
37187
37352
|
}
|
|
37188
37353
|
function detectCheckstyle(cwd) {
|
|
37189
|
-
const hasMaven = fs9.existsSync(
|
|
37190
|
-
const hasGradle = fs9.existsSync(
|
|
37191
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs9.existsSync(
|
|
37354
|
+
const hasMaven = fs9.existsSync(path19.join(cwd, "pom.xml"));
|
|
37355
|
+
const hasGradle = fs9.existsSync(path19.join(cwd, "build.gradle")) || fs9.existsSync(path19.join(cwd, "build.gradle.kts"));
|
|
37356
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs9.existsSync(path19.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
37192
37357
|
return (hasMaven || hasGradle) && hasBinary;
|
|
37193
37358
|
}
|
|
37194
37359
|
function detectKtlint(cwd) {
|
|
37195
|
-
const hasKotlin = fs9.existsSync(
|
|
37360
|
+
const hasKotlin = fs9.existsSync(path19.join(cwd, "build.gradle.kts")) || fs9.existsSync(path19.join(cwd, "build.gradle")) || (() => {
|
|
37196
37361
|
try {
|
|
37197
37362
|
return fs9.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
37198
37363
|
} catch {
|
|
@@ -37211,11 +37376,11 @@ function detectDotnetFormat(cwd) {
|
|
|
37211
37376
|
}
|
|
37212
37377
|
}
|
|
37213
37378
|
function detectCppcheck(cwd) {
|
|
37214
|
-
if (fs9.existsSync(
|
|
37379
|
+
if (fs9.existsSync(path19.join(cwd, "CMakeLists.txt"))) {
|
|
37215
37380
|
return isCommandAvailable("cppcheck");
|
|
37216
37381
|
}
|
|
37217
37382
|
try {
|
|
37218
|
-
const dirsToCheck = [cwd,
|
|
37383
|
+
const dirsToCheck = [cwd, path19.join(cwd, "src")];
|
|
37219
37384
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
37220
37385
|
try {
|
|
37221
37386
|
return fs9.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
@@ -37229,13 +37394,13 @@ function detectCppcheck(cwd) {
|
|
|
37229
37394
|
}
|
|
37230
37395
|
}
|
|
37231
37396
|
function detectSwiftlint(cwd) {
|
|
37232
|
-
return fs9.existsSync(
|
|
37397
|
+
return fs9.existsSync(path19.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
37233
37398
|
}
|
|
37234
37399
|
function detectDartAnalyze(cwd) {
|
|
37235
|
-
return fs9.existsSync(
|
|
37400
|
+
return fs9.existsSync(path19.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
37236
37401
|
}
|
|
37237
37402
|
function detectRubocop(cwd) {
|
|
37238
|
-
return (fs9.existsSync(
|
|
37403
|
+
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"));
|
|
37239
37404
|
}
|
|
37240
37405
|
function detectAdditionalLinter(cwd) {
|
|
37241
37406
|
if (detectRuff(cwd))
|
|
@@ -37263,10 +37428,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
37263
37428
|
function findBinInAncestors(startDir, binName) {
|
|
37264
37429
|
let dir = startDir;
|
|
37265
37430
|
while (true) {
|
|
37266
|
-
const candidate =
|
|
37431
|
+
const candidate = path19.join(dir, "node_modules", ".bin", binName);
|
|
37267
37432
|
if (fs9.existsSync(candidate))
|
|
37268
37433
|
return candidate;
|
|
37269
|
-
const parent =
|
|
37434
|
+
const parent = path19.dirname(dir);
|
|
37270
37435
|
if (parent === dir)
|
|
37271
37436
|
break;
|
|
37272
37437
|
dir = parent;
|
|
@@ -37275,10 +37440,10 @@ function findBinInAncestors(startDir, binName) {
|
|
|
37275
37440
|
}
|
|
37276
37441
|
function findBinInEnvPath(binName) {
|
|
37277
37442
|
const searchPath = process.env.PATH ?? "";
|
|
37278
|
-
for (const dir of searchPath.split(
|
|
37443
|
+
for (const dir of searchPath.split(path19.delimiter)) {
|
|
37279
37444
|
if (!dir)
|
|
37280
37445
|
continue;
|
|
37281
|
-
const candidate =
|
|
37446
|
+
const candidate = path19.join(dir, binName);
|
|
37282
37447
|
if (fs9.existsSync(candidate))
|
|
37283
37448
|
return candidate;
|
|
37284
37449
|
}
|
|
@@ -37291,13 +37456,13 @@ async function detectAvailableLinter(directory) {
|
|
|
37291
37456
|
return null;
|
|
37292
37457
|
const projectDir = directory;
|
|
37293
37458
|
const isWindows = process.platform === "win32";
|
|
37294
|
-
const biomeBin = isWindows ?
|
|
37295
|
-
const eslintBin = isWindows ?
|
|
37459
|
+
const biomeBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "biome.EXE") : path19.join(projectDir, "node_modules", ".bin", "biome");
|
|
37460
|
+
const eslintBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path19.join(projectDir, "node_modules", ".bin", "eslint");
|
|
37296
37461
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
37297
37462
|
if (localResult)
|
|
37298
37463
|
return localResult;
|
|
37299
|
-
const biomeAncestor = findBinInAncestors(
|
|
37300
|
-
const eslintAncestor = findBinInAncestors(
|
|
37464
|
+
const biomeAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
37465
|
+
const eslintAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
37301
37466
|
if (biomeAncestor || eslintAncestor) {
|
|
37302
37467
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
37303
37468
|
}
|
|
@@ -37505,7 +37670,7 @@ For Rust: rustup component add clippy`
|
|
|
37505
37670
|
|
|
37506
37671
|
// src/tools/secretscan.ts
|
|
37507
37672
|
import * as fs10 from "fs";
|
|
37508
|
-
import * as
|
|
37673
|
+
import * as path20 from "path";
|
|
37509
37674
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
37510
37675
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
37511
37676
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -37732,7 +37897,7 @@ function isGlobOrPathPattern(pattern) {
|
|
|
37732
37897
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
37733
37898
|
}
|
|
37734
37899
|
function loadSecretScanIgnore(scanDir) {
|
|
37735
|
-
const ignorePath =
|
|
37900
|
+
const ignorePath = path20.join(scanDir, ".secretscanignore");
|
|
37736
37901
|
try {
|
|
37737
37902
|
if (!fs10.existsSync(ignorePath))
|
|
37738
37903
|
return [];
|
|
@@ -37755,7 +37920,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
37755
37920
|
if (exactNames.has(entry))
|
|
37756
37921
|
return true;
|
|
37757
37922
|
for (const pattern of globPatterns) {
|
|
37758
|
-
if (
|
|
37923
|
+
if (path20.matchesGlob(relPath, pattern))
|
|
37759
37924
|
return true;
|
|
37760
37925
|
}
|
|
37761
37926
|
return false;
|
|
@@ -37776,7 +37941,7 @@ function validateDirectoryInput(dir) {
|
|
|
37776
37941
|
return null;
|
|
37777
37942
|
}
|
|
37778
37943
|
function isBinaryFile(filePath, buffer) {
|
|
37779
|
-
const ext =
|
|
37944
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
37780
37945
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37781
37946
|
return true;
|
|
37782
37947
|
}
|
|
@@ -37913,9 +38078,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
37913
38078
|
return false;
|
|
37914
38079
|
}
|
|
37915
38080
|
function isPathWithinScope(realPath, scanDir) {
|
|
37916
|
-
const resolvedScanDir =
|
|
37917
|
-
const resolvedRealPath =
|
|
37918
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
38081
|
+
const resolvedScanDir = path20.resolve(scanDir);
|
|
38082
|
+
const resolvedRealPath = path20.resolve(realPath);
|
|
38083
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path20.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
37919
38084
|
}
|
|
37920
38085
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
37921
38086
|
skippedDirs: 0,
|
|
@@ -37941,8 +38106,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37941
38106
|
return a.localeCompare(b);
|
|
37942
38107
|
});
|
|
37943
38108
|
for (const entry of entries) {
|
|
37944
|
-
const fullPath =
|
|
37945
|
-
const relPath =
|
|
38109
|
+
const fullPath = path20.join(dir, entry);
|
|
38110
|
+
const relPath = path20.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
37946
38111
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
37947
38112
|
stats.skippedDirs++;
|
|
37948
38113
|
continue;
|
|
@@ -37977,7 +38142,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37977
38142
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
37978
38143
|
files.push(...subFiles);
|
|
37979
38144
|
} else if (lstat.isFile()) {
|
|
37980
|
-
const ext =
|
|
38145
|
+
const ext = path20.extname(fullPath).toLowerCase();
|
|
37981
38146
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37982
38147
|
files.push(fullPath);
|
|
37983
38148
|
} else {
|
|
@@ -38043,7 +38208,7 @@ var secretscan = createSwarmTool({
|
|
|
38043
38208
|
}
|
|
38044
38209
|
}
|
|
38045
38210
|
try {
|
|
38046
|
-
const _scanDirRaw =
|
|
38211
|
+
const _scanDirRaw = path20.resolve(directory);
|
|
38047
38212
|
const scanDir = (() => {
|
|
38048
38213
|
try {
|
|
38049
38214
|
return fs10.realpathSync(_scanDirRaw);
|
|
@@ -38201,11 +38366,11 @@ async function runSecretscan(directory) {
|
|
|
38201
38366
|
|
|
38202
38367
|
// src/tools/test-runner.ts
|
|
38203
38368
|
import * as fs12 from "fs";
|
|
38204
|
-
import * as
|
|
38369
|
+
import * as path22 from "path";
|
|
38205
38370
|
|
|
38206
38371
|
// src/tools/resolve-working-directory.ts
|
|
38207
38372
|
import * as fs11 from "fs";
|
|
38208
|
-
import * as
|
|
38373
|
+
import * as path21 from "path";
|
|
38209
38374
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
38210
38375
|
if (workingDirectory == null || workingDirectory === "") {
|
|
38211
38376
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -38225,15 +38390,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
38225
38390
|
};
|
|
38226
38391
|
}
|
|
38227
38392
|
}
|
|
38228
|
-
const normalizedDir =
|
|
38229
|
-
const pathParts = normalizedDir.split(
|
|
38393
|
+
const normalizedDir = path21.normalize(workingDirectory);
|
|
38394
|
+
const pathParts = normalizedDir.split(path21.sep);
|
|
38230
38395
|
if (pathParts.includes("..")) {
|
|
38231
38396
|
return {
|
|
38232
38397
|
success: false,
|
|
38233
38398
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
38234
38399
|
};
|
|
38235
38400
|
}
|
|
38236
|
-
const resolvedDir =
|
|
38401
|
+
const resolvedDir = path21.resolve(normalizedDir);
|
|
38237
38402
|
try {
|
|
38238
38403
|
const realPath = fs11.realpathSync(resolvedDir);
|
|
38239
38404
|
return { success: true, directory: realPath };
|
|
@@ -38316,14 +38481,14 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
38316
38481
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
38317
38482
|
}
|
|
38318
38483
|
function detectGoTest(cwd) {
|
|
38319
|
-
return fs12.existsSync(
|
|
38484
|
+
return fs12.existsSync(path22.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
38320
38485
|
}
|
|
38321
38486
|
function detectJavaMaven(cwd) {
|
|
38322
|
-
return fs12.existsSync(
|
|
38487
|
+
return fs12.existsSync(path22.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
38323
38488
|
}
|
|
38324
38489
|
function detectGradle(cwd) {
|
|
38325
|
-
const hasBuildFile = fs12.existsSync(
|
|
38326
|
-
const hasGradlew = fs12.existsSync(
|
|
38490
|
+
const hasBuildFile = fs12.existsSync(path22.join(cwd, "build.gradle")) || fs12.existsSync(path22.join(cwd, "build.gradle.kts"));
|
|
38491
|
+
const hasGradlew = fs12.existsSync(path22.join(cwd, "gradlew")) || fs12.existsSync(path22.join(cwd, "gradlew.bat"));
|
|
38327
38492
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
38328
38493
|
}
|
|
38329
38494
|
function detectDotnetTest(cwd) {
|
|
@@ -38336,30 +38501,30 @@ function detectDotnetTest(cwd) {
|
|
|
38336
38501
|
}
|
|
38337
38502
|
}
|
|
38338
38503
|
function detectCTest(cwd) {
|
|
38339
|
-
const hasSource = fs12.existsSync(
|
|
38340
|
-
const hasBuildCache = fs12.existsSync(
|
|
38504
|
+
const hasSource = fs12.existsSync(path22.join(cwd, "CMakeLists.txt"));
|
|
38505
|
+
const hasBuildCache = fs12.existsSync(path22.join(cwd, "CMakeCache.txt")) || fs12.existsSync(path22.join(cwd, "build", "CMakeCache.txt"));
|
|
38341
38506
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
38342
38507
|
}
|
|
38343
38508
|
function detectSwiftTest(cwd) {
|
|
38344
|
-
return fs12.existsSync(
|
|
38509
|
+
return fs12.existsSync(path22.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
38345
38510
|
}
|
|
38346
38511
|
function detectDartTest(cwd) {
|
|
38347
|
-
return fs12.existsSync(
|
|
38512
|
+
return fs12.existsSync(path22.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
38348
38513
|
}
|
|
38349
38514
|
function detectRSpec(cwd) {
|
|
38350
|
-
const hasRSpecFile = fs12.existsSync(
|
|
38351
|
-
const hasGemfile = fs12.existsSync(
|
|
38352
|
-
const hasSpecDir = fs12.existsSync(
|
|
38515
|
+
const hasRSpecFile = fs12.existsSync(path22.join(cwd, ".rspec"));
|
|
38516
|
+
const hasGemfile = fs12.existsSync(path22.join(cwd, "Gemfile"));
|
|
38517
|
+
const hasSpecDir = fs12.existsSync(path22.join(cwd, "spec"));
|
|
38353
38518
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
38354
38519
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
38355
38520
|
}
|
|
38356
38521
|
function detectMinitest(cwd) {
|
|
38357
|
-
return fs12.existsSync(
|
|
38522
|
+
return fs12.existsSync(path22.join(cwd, "test")) && (fs12.existsSync(path22.join(cwd, "Gemfile")) || fs12.existsSync(path22.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
38358
38523
|
}
|
|
38359
38524
|
async function detectTestFramework(cwd) {
|
|
38360
38525
|
const baseDir = cwd;
|
|
38361
38526
|
try {
|
|
38362
|
-
const packageJsonPath =
|
|
38527
|
+
const packageJsonPath = path22.join(baseDir, "package.json");
|
|
38363
38528
|
if (fs12.existsSync(packageJsonPath)) {
|
|
38364
38529
|
const content = fs12.readFileSync(packageJsonPath, "utf-8");
|
|
38365
38530
|
const pkg = JSON.parse(content);
|
|
@@ -38380,16 +38545,16 @@ async function detectTestFramework(cwd) {
|
|
|
38380
38545
|
return "jest";
|
|
38381
38546
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
38382
38547
|
return "mocha";
|
|
38383
|
-
if (fs12.existsSync(
|
|
38548
|
+
if (fs12.existsSync(path22.join(baseDir, "bun.lockb")) || fs12.existsSync(path22.join(baseDir, "bun.lock"))) {
|
|
38384
38549
|
if (scripts.test?.includes("bun"))
|
|
38385
38550
|
return "bun";
|
|
38386
38551
|
}
|
|
38387
38552
|
}
|
|
38388
38553
|
} catch {}
|
|
38389
38554
|
try {
|
|
38390
|
-
const pyprojectTomlPath =
|
|
38391
|
-
const setupCfgPath =
|
|
38392
|
-
const requirementsTxtPath =
|
|
38555
|
+
const pyprojectTomlPath = path22.join(baseDir, "pyproject.toml");
|
|
38556
|
+
const setupCfgPath = path22.join(baseDir, "setup.cfg");
|
|
38557
|
+
const requirementsTxtPath = path22.join(baseDir, "requirements.txt");
|
|
38393
38558
|
if (fs12.existsSync(pyprojectTomlPath)) {
|
|
38394
38559
|
const content = fs12.readFileSync(pyprojectTomlPath, "utf-8");
|
|
38395
38560
|
if (content.includes("[tool.pytest"))
|
|
@@ -38409,7 +38574,7 @@ async function detectTestFramework(cwd) {
|
|
|
38409
38574
|
}
|
|
38410
38575
|
} catch {}
|
|
38411
38576
|
try {
|
|
38412
|
-
const cargoTomlPath =
|
|
38577
|
+
const cargoTomlPath = path22.join(baseDir, "Cargo.toml");
|
|
38413
38578
|
if (fs12.existsSync(cargoTomlPath)) {
|
|
38414
38579
|
const content = fs12.readFileSync(cargoTomlPath, "utf-8");
|
|
38415
38580
|
if (content.includes("[dev-dependencies]")) {
|
|
@@ -38420,9 +38585,9 @@ async function detectTestFramework(cwd) {
|
|
|
38420
38585
|
}
|
|
38421
38586
|
} catch {}
|
|
38422
38587
|
try {
|
|
38423
|
-
const pesterConfigPath =
|
|
38424
|
-
const pesterConfigJsonPath =
|
|
38425
|
-
const pesterPs1Path =
|
|
38588
|
+
const pesterConfigPath = path22.join(baseDir, "pester.config.ps1");
|
|
38589
|
+
const pesterConfigJsonPath = path22.join(baseDir, "pester.config.ps1.json");
|
|
38590
|
+
const pesterPs1Path = path22.join(baseDir, "tests.ps1");
|
|
38426
38591
|
if (fs12.existsSync(pesterConfigPath) || fs12.existsSync(pesterConfigJsonPath) || fs12.existsSync(pesterPs1Path)) {
|
|
38427
38592
|
return "pester";
|
|
38428
38593
|
}
|
|
@@ -38474,8 +38639,8 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38474
38639
|
const testFiles = [];
|
|
38475
38640
|
for (const file3 of sourceFiles) {
|
|
38476
38641
|
const normalizedPath = file3.replace(/\\/g, "/");
|
|
38477
|
-
const basename4 =
|
|
38478
|
-
const dirname10 =
|
|
38642
|
+
const basename4 = path22.basename(file3);
|
|
38643
|
+
const dirname10 = path22.dirname(file3);
|
|
38479
38644
|
if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
|
|
38480
38645
|
if (!testFiles.includes(file3)) {
|
|
38481
38646
|
testFiles.push(file3);
|
|
@@ -38484,13 +38649,13 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38484
38649
|
}
|
|
38485
38650
|
for (const _pattern of TEST_PATTERNS) {
|
|
38486
38651
|
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
38487
|
-
const ext =
|
|
38652
|
+
const ext = path22.extname(basename4);
|
|
38488
38653
|
const possibleTestFiles = [
|
|
38489
|
-
|
|
38490
|
-
|
|
38491
|
-
|
|
38492
|
-
|
|
38493
|
-
|
|
38654
|
+
path22.join(dirname10, `${nameWithoutExt}.spec${ext}`),
|
|
38655
|
+
path22.join(dirname10, `${nameWithoutExt}.test${ext}`),
|
|
38656
|
+
path22.join(dirname10, "__tests__", `${nameWithoutExt}${ext}`),
|
|
38657
|
+
path22.join(dirname10, "tests", `${nameWithoutExt}${ext}`),
|
|
38658
|
+
path22.join(dirname10, "test", `${nameWithoutExt}${ext}`)
|
|
38494
38659
|
];
|
|
38495
38660
|
for (const testFile of possibleTestFiles) {
|
|
38496
38661
|
if (fs12.existsSync(testFile) && !testFiles.includes(testFile)) {
|
|
@@ -38510,7 +38675,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38510
38675
|
for (const testFile of candidateTestFiles) {
|
|
38511
38676
|
try {
|
|
38512
38677
|
const content = fs12.readFileSync(testFile, "utf-8");
|
|
38513
|
-
const testDir =
|
|
38678
|
+
const testDir = path22.dirname(testFile);
|
|
38514
38679
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
38515
38680
|
let match;
|
|
38516
38681
|
match = importRegex.exec(content);
|
|
@@ -38518,8 +38683,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38518
38683
|
const importPath = match[1];
|
|
38519
38684
|
let resolvedImport;
|
|
38520
38685
|
if (importPath.startsWith(".")) {
|
|
38521
|
-
resolvedImport =
|
|
38522
|
-
const existingExt =
|
|
38686
|
+
resolvedImport = path22.resolve(testDir, importPath);
|
|
38687
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38523
38688
|
if (!existingExt) {
|
|
38524
38689
|
for (const extToTry of [
|
|
38525
38690
|
".ts",
|
|
@@ -38539,12 +38704,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38539
38704
|
} else {
|
|
38540
38705
|
continue;
|
|
38541
38706
|
}
|
|
38542
|
-
const importBasename =
|
|
38543
|
-
const importDir =
|
|
38707
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38708
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38544
38709
|
for (const sourceFile of sourceFiles) {
|
|
38545
|
-
const sourceDir =
|
|
38546
|
-
const sourceBasename =
|
|
38547
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38710
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38711
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38712
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38548
38713
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38549
38714
|
if (!testFiles.includes(testFile)) {
|
|
38550
38715
|
testFiles.push(testFile);
|
|
@@ -38559,8 +38724,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38559
38724
|
while (match !== null) {
|
|
38560
38725
|
const importPath = match[1];
|
|
38561
38726
|
if (importPath.startsWith(".")) {
|
|
38562
|
-
let resolvedImport =
|
|
38563
|
-
const existingExt =
|
|
38727
|
+
let resolvedImport = path22.resolve(testDir, importPath);
|
|
38728
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38564
38729
|
if (!existingExt) {
|
|
38565
38730
|
for (const extToTry of [
|
|
38566
38731
|
".ts",
|
|
@@ -38577,12 +38742,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38577
38742
|
}
|
|
38578
38743
|
}
|
|
38579
38744
|
}
|
|
38580
|
-
const importDir =
|
|
38581
|
-
const importBasename =
|
|
38745
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38746
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38582
38747
|
for (const sourceFile of sourceFiles) {
|
|
38583
|
-
const sourceDir =
|
|
38584
|
-
const sourceBasename =
|
|
38585
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38748
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38749
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38750
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38586
38751
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38587
38752
|
if (!testFiles.includes(testFile)) {
|
|
38588
38753
|
testFiles.push(testFile);
|
|
@@ -38667,8 +38832,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38667
38832
|
return ["mvn", "test"];
|
|
38668
38833
|
case "gradle": {
|
|
38669
38834
|
const isWindows = process.platform === "win32";
|
|
38670
|
-
const hasGradlewBat = fs12.existsSync(
|
|
38671
|
-
const hasGradlew = fs12.existsSync(
|
|
38835
|
+
const hasGradlewBat = fs12.existsSync(path22.join(baseDir, "gradlew.bat"));
|
|
38836
|
+
const hasGradlew = fs12.existsSync(path22.join(baseDir, "gradlew"));
|
|
38672
38837
|
if (hasGradlewBat && isWindows)
|
|
38673
38838
|
return ["gradlew.bat", "test"];
|
|
38674
38839
|
if (hasGradlew)
|
|
@@ -38685,7 +38850,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38685
38850
|
"cmake-build-release",
|
|
38686
38851
|
"out"
|
|
38687
38852
|
];
|
|
38688
|
-
const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(
|
|
38853
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(path22.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
38689
38854
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
38690
38855
|
}
|
|
38691
38856
|
case "swift-test":
|
|
@@ -39227,7 +39392,7 @@ var test_runner = createSwarmTool({
|
|
|
39227
39392
|
let effectiveScope = scope;
|
|
39228
39393
|
if (scope === "all") {} else if (scope === "convention") {
|
|
39229
39394
|
const sourceFiles = args.files.filter((f) => {
|
|
39230
|
-
const ext =
|
|
39395
|
+
const ext = path22.extname(f).toLowerCase();
|
|
39231
39396
|
return SOURCE_EXTENSIONS.has(ext);
|
|
39232
39397
|
});
|
|
39233
39398
|
if (sourceFiles.length === 0) {
|
|
@@ -39243,7 +39408,7 @@ var test_runner = createSwarmTool({
|
|
|
39243
39408
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
39244
39409
|
} else if (scope === "graph") {
|
|
39245
39410
|
const sourceFiles = args.files.filter((f) => {
|
|
39246
|
-
const ext =
|
|
39411
|
+
const ext = path22.extname(f).toLowerCase();
|
|
39247
39412
|
return SOURCE_EXTENSIONS.has(ext);
|
|
39248
39413
|
});
|
|
39249
39414
|
if (sourceFiles.length === 0) {
|
|
@@ -39314,8 +39479,8 @@ function validateDirectoryPath(dir) {
|
|
|
39314
39479
|
if (dir.includes("..")) {
|
|
39315
39480
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
39316
39481
|
}
|
|
39317
|
-
const normalized =
|
|
39318
|
-
const absolutePath =
|
|
39482
|
+
const normalized = path23.normalize(dir);
|
|
39483
|
+
const absolutePath = path23.isAbsolute(normalized) ? normalized : path23.resolve(normalized);
|
|
39319
39484
|
return absolutePath;
|
|
39320
39485
|
}
|
|
39321
39486
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -39338,7 +39503,7 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
39338
39503
|
}
|
|
39339
39504
|
function getPackageVersion(dir) {
|
|
39340
39505
|
try {
|
|
39341
|
-
const packagePath =
|
|
39506
|
+
const packagePath = path23.join(dir, "package.json");
|
|
39342
39507
|
if (fs13.existsSync(packagePath)) {
|
|
39343
39508
|
const content = fs13.readFileSync(packagePath, "utf-8");
|
|
39344
39509
|
const pkg = JSON.parse(content);
|
|
@@ -39349,7 +39514,7 @@ function getPackageVersion(dir) {
|
|
|
39349
39514
|
}
|
|
39350
39515
|
function getChangelogVersion(dir) {
|
|
39351
39516
|
try {
|
|
39352
|
-
const changelogPath =
|
|
39517
|
+
const changelogPath = path23.join(dir, "CHANGELOG.md");
|
|
39353
39518
|
if (fs13.existsSync(changelogPath)) {
|
|
39354
39519
|
const content = fs13.readFileSync(changelogPath, "utf-8");
|
|
39355
39520
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
@@ -39363,7 +39528,7 @@ function getChangelogVersion(dir) {
|
|
|
39363
39528
|
function getVersionFileVersion(dir) {
|
|
39364
39529
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
39365
39530
|
for (const file3 of possibleFiles) {
|
|
39366
|
-
const filePath =
|
|
39531
|
+
const filePath = path23.join(dir, file3);
|
|
39367
39532
|
if (fs13.existsSync(filePath)) {
|
|
39368
39533
|
try {
|
|
39369
39534
|
const content = fs13.readFileSync(filePath, "utf-8").trim();
|
|
@@ -39861,7 +40026,7 @@ async function handlePreflightCommand(directory, _args) {
|
|
|
39861
40026
|
// src/knowledge/hive-promoter.ts
|
|
39862
40027
|
import * as fs14 from "fs";
|
|
39863
40028
|
import * as os5 from "os";
|
|
39864
|
-
import * as
|
|
40029
|
+
import * as path24 from "path";
|
|
39865
40030
|
var DANGEROUS_PATTERNS = [
|
|
39866
40031
|
[/rm\s+-rf/, "rm\\s+-rf"],
|
|
39867
40032
|
[/:\s*!\s*\|/, ":\\s*!\\s*\\|"],
|
|
@@ -39907,13 +40072,13 @@ function getHiveFilePath() {
|
|
|
39907
40072
|
const home = os5.homedir();
|
|
39908
40073
|
let dataDir;
|
|
39909
40074
|
if (platform === "win32") {
|
|
39910
|
-
dataDir =
|
|
40075
|
+
dataDir = path24.join(process.env.LOCALAPPDATA || path24.join(home, "AppData", "Local"), "opencode-swarm", "Data");
|
|
39911
40076
|
} else if (platform === "darwin") {
|
|
39912
|
-
dataDir =
|
|
40077
|
+
dataDir = path24.join(home, "Library", "Application Support", "opencode-swarm");
|
|
39913
40078
|
} else {
|
|
39914
|
-
dataDir =
|
|
40079
|
+
dataDir = path24.join(process.env.XDG_DATA_HOME || path24.join(home, ".local", "share"), "opencode-swarm");
|
|
39915
40080
|
}
|
|
39916
|
-
return
|
|
40081
|
+
return path24.join(dataDir, "hive-knowledge.jsonl");
|
|
39917
40082
|
}
|
|
39918
40083
|
async function promoteToHive(_directory, lesson, category) {
|
|
39919
40084
|
const trimmed = (lesson ?? "").trim();
|
|
@@ -39925,7 +40090,7 @@ async function promoteToHive(_directory, lesson, category) {
|
|
|
39925
40090
|
throw new Error(`Lesson rejected by validator: ${validation.reason}`);
|
|
39926
40091
|
}
|
|
39927
40092
|
const hivePath = getHiveFilePath();
|
|
39928
|
-
const hiveDir =
|
|
40093
|
+
const hiveDir = path24.dirname(hivePath);
|
|
39929
40094
|
if (!fs14.existsSync(hiveDir)) {
|
|
39930
40095
|
fs14.mkdirSync(hiveDir, { recursive: true });
|
|
39931
40096
|
}
|
|
@@ -39947,7 +40112,7 @@ async function promoteToHive(_directory, lesson, category) {
|
|
|
39947
40112
|
return `Promoted to hive: "${preview}" (confidence: 1.0, source: manual)`;
|
|
39948
40113
|
}
|
|
39949
40114
|
async function promoteFromSwarm(directory, lessonId) {
|
|
39950
|
-
const knowledgePath =
|
|
40115
|
+
const knowledgePath = path24.join(directory, ".swarm", "knowledge.jsonl");
|
|
39951
40116
|
const entries = [];
|
|
39952
40117
|
if (fs14.existsSync(knowledgePath)) {
|
|
39953
40118
|
const content = fs14.readFileSync(knowledgePath, "utf-8");
|
|
@@ -39974,7 +40139,7 @@ async function promoteFromSwarm(directory, lessonId) {
|
|
|
39974
40139
|
throw new Error(`Lesson rejected by validator: ${validation.reason}`);
|
|
39975
40140
|
}
|
|
39976
40141
|
const hivePath = getHiveFilePath();
|
|
39977
|
-
const hiveDir =
|
|
40142
|
+
const hiveDir = path24.dirname(hivePath);
|
|
39978
40143
|
if (!fs14.existsSync(hiveDir)) {
|
|
39979
40144
|
fs14.mkdirSync(hiveDir, { recursive: true });
|
|
39980
40145
|
}
|
|
@@ -40785,7 +40950,7 @@ async function handleResetCommand(directory, args) {
|
|
|
40785
40950
|
// src/commands/reset-session.ts
|
|
40786
40951
|
init_utils2();
|
|
40787
40952
|
import * as fs16 from "fs";
|
|
40788
|
-
import * as
|
|
40953
|
+
import * as path25 from "path";
|
|
40789
40954
|
async function handleResetSessionCommand(directory, _args) {
|
|
40790
40955
|
const results = [];
|
|
40791
40956
|
try {
|
|
@@ -40800,13 +40965,13 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40800
40965
|
results.push("\u274C Failed to delete state.json");
|
|
40801
40966
|
}
|
|
40802
40967
|
try {
|
|
40803
|
-
const sessionDir =
|
|
40968
|
+
const sessionDir = path25.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
40804
40969
|
if (fs16.existsSync(sessionDir)) {
|
|
40805
40970
|
const files = fs16.readdirSync(sessionDir);
|
|
40806
40971
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
40807
40972
|
let deletedCount = 0;
|
|
40808
40973
|
for (const file3 of otherFiles) {
|
|
40809
|
-
const filePath =
|
|
40974
|
+
const filePath = path25.join(sessionDir, file3);
|
|
40810
40975
|
if (fs16.lstatSync(filePath).isFile()) {
|
|
40811
40976
|
fs16.unlinkSync(filePath);
|
|
40812
40977
|
deletedCount++;
|
|
@@ -40836,7 +41001,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40836
41001
|
// src/summaries/manager.ts
|
|
40837
41002
|
init_utils2();
|
|
40838
41003
|
init_utils();
|
|
40839
|
-
import * as
|
|
41004
|
+
import * as path26 from "path";
|
|
40840
41005
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
40841
41006
|
function sanitizeSummaryId(id) {
|
|
40842
41007
|
if (!id || id.length === 0) {
|
|
@@ -40860,7 +41025,7 @@ function sanitizeSummaryId(id) {
|
|
|
40860
41025
|
}
|
|
40861
41026
|
async function loadFullOutput(directory, id) {
|
|
40862
41027
|
const sanitizedId = sanitizeSummaryId(id);
|
|
40863
|
-
const relativePath =
|
|
41028
|
+
const relativePath = path26.join("summaries", `${sanitizedId}.json`);
|
|
40864
41029
|
validateSwarmPath(directory, relativePath);
|
|
40865
41030
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
40866
41031
|
if (content === null) {
|
|
@@ -40914,7 +41079,7 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
40914
41079
|
// src/commands/rollback.ts
|
|
40915
41080
|
init_utils2();
|
|
40916
41081
|
import * as fs17 from "fs";
|
|
40917
|
-
import * as
|
|
41082
|
+
import * as path27 from "path";
|
|
40918
41083
|
async function handleRollbackCommand(directory, args) {
|
|
40919
41084
|
const phaseArg = args[0];
|
|
40920
41085
|
if (!phaseArg) {
|
|
@@ -40972,8 +41137,8 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40972
41137
|
const successes = [];
|
|
40973
41138
|
const failures = [];
|
|
40974
41139
|
for (const file3 of checkpointFiles) {
|
|
40975
|
-
const src =
|
|
40976
|
-
const dest =
|
|
41140
|
+
const src = path27.join(checkpointDir, file3);
|
|
41141
|
+
const dest = path27.join(swarmDir, file3);
|
|
40977
41142
|
try {
|
|
40978
41143
|
fs17.cpSync(src, dest, { recursive: true, force: true });
|
|
40979
41144
|
successes.push(file3);
|
|
@@ -41036,9 +41201,9 @@ async function handleSimulateCommand(directory, args) {
|
|
|
41036
41201
|
const report = reportLines.filter(Boolean).join(`
|
|
41037
41202
|
`);
|
|
41038
41203
|
const fs18 = await import("fs/promises");
|
|
41039
|
-
const
|
|
41040
|
-
const reportPath =
|
|
41041
|
-
await fs18.mkdir(
|
|
41204
|
+
const path28 = await import("path");
|
|
41205
|
+
const reportPath = path28.join(directory, ".swarm", "simulate-report.md");
|
|
41206
|
+
await fs18.mkdir(path28.dirname(reportPath), { recursive: true });
|
|
41042
41207
|
await fs18.writeFile(reportPath, report, "utf-8");
|
|
41043
41208
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
41044
41209
|
}
|
|
@@ -41521,10 +41686,10 @@ function resolveCommand(tokens) {
|
|
|
41521
41686
|
}
|
|
41522
41687
|
|
|
41523
41688
|
// src/cli/index.ts
|
|
41524
|
-
var CONFIG_DIR =
|
|
41525
|
-
var OPENCODE_CONFIG_PATH =
|
|
41526
|
-
var PLUGIN_CONFIG_PATH =
|
|
41527
|
-
var PROMPTS_DIR =
|
|
41689
|
+
var CONFIG_DIR = path28.join(process.env.XDG_CONFIG_HOME || path28.join(os6.homedir(), ".config"), "opencode");
|
|
41690
|
+
var OPENCODE_CONFIG_PATH = path28.join(CONFIG_DIR, "opencode.json");
|
|
41691
|
+
var PLUGIN_CONFIG_PATH = path28.join(CONFIG_DIR, "opencode-swarm.json");
|
|
41692
|
+
var PROMPTS_DIR = path28.join(CONFIG_DIR, "opencode-swarm");
|
|
41528
41693
|
function ensureDir(dir) {
|
|
41529
41694
|
if (!fs18.existsSync(dir)) {
|
|
41530
41695
|
fs18.mkdirSync(dir, { recursive: true });
|
|
@@ -41548,7 +41713,7 @@ async function install() {
|
|
|
41548
41713
|
`);
|
|
41549
41714
|
ensureDir(CONFIG_DIR);
|
|
41550
41715
|
ensureDir(PROMPTS_DIR);
|
|
41551
|
-
const LEGACY_CONFIG_PATH =
|
|
41716
|
+
const LEGACY_CONFIG_PATH = path28.join(CONFIG_DIR, "config.json");
|
|
41552
41717
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41553
41718
|
if (!opencodeConfig) {
|
|
41554
41719
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|